fix build on MSVC
[imager-screenshot.git] / scwin32.c
CommitLineData
0ddb7051
TC
1#include "imext.h"
2#include <windows.h>
3#include <string.h>
87cd516f 4#include "imss.h"
0ddb7051 5
ece126f9
TC
6/* the SDK headers supplied with cygwin, and with some older strawberry perls */
7#ifndef DISPLAY_DEVICE_ACTIVE
8#define DISPLAY_DEVICE_ACTIVE 1
9#endif
10
c50a933e
TC
11static void
12i_push_win32_errorf(DWORD msg_id, char const *fmt, ...) {
13 va_list args;
14 LPSTR msg;
15 char buf[1000];
16
17 va_start(args, fmt);
18 vsprintf(buf, fmt, args);
19 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
20 NULL,
21 msg_id,
22 0, /* LANGID */
23 (LPSTR)&msg,
24 0,
25 NULL)) {
26 strcat(buf, msg);
27 LocalFree(msg);
28 }
29 else {
30 sprintf(buf+strlen(buf), "%#010x", msg_id);
31 }
32 i_push_error(msg_id, buf);
33 va_end(args);
34}
35
6d37c2c9 36struct monitor_ctx {
ece126f9
TC
37 i_img *out;
38 i_img_dim orig_x, orig_y;
6d37c2c9 39 int good;
ece126f9
TC
40};
41
42static int
43display_to_img(HDC dc, i_img *im, const RECT *src, int dest_x, int dest_y) {
44 HBITMAP work_bmp;
45 HDC bmdc;
46 HBITMAP old_dc_bmp;
47 i_img_dim width = src->right - src->left;
48 i_img_dim height = src->bottom - src->top;
49 int result = 0;
50 BITMAPINFO bmi;
51 unsigned char *di_bits;
52
53 work_bmp = CreateCompatibleBitmap(dc, width, height);
54 bmdc = CreateCompatibleDC(dc);
55 old_dc_bmp = SelectObject(bmdc, work_bmp);
56 BitBlt(bmdc, 0, 0, width, height, dc, src->left, src->top, SRCCOPY);
57
58 /* make a dib */
59 memset(&bmi, 0, sizeof(bmi));
60 bmi.bmiHeader.biSize = sizeof(bmi);
61 bmi.bmiHeader.biWidth = width;
62 bmi.bmiHeader.biHeight = -height;
63 bmi.bmiHeader.biPlanes = 1;
64 bmi.bmiHeader.biBitCount = 32;
65 bmi.bmiHeader.biCompression = BI_RGB;
66
67 di_bits = mymalloc(4 * width * height);
68 if (GetDIBits(bmdc, work_bmp, 0, height, di_bits, &bmi, DIB_RGB_COLORS)) {
69 i_color *line = mymalloc(sizeof(i_color) * width);
70 i_color *cp;
71 int x, y;
72 unsigned char *ch_pp = di_bits;
73 for (y = 0; y < height; ++y) {
74 cp = line;
75 for (x = 0; x < width; ++x) {
6d37c2c9
TC
76 cp->rgba.b = *ch_pp++;
77 cp->rgba.g = *ch_pp++;
78 cp->rgba.r = *ch_pp++;
79 cp->rgba.a = 255;
ece126f9
TC
80 ch_pp++;
81 cp++;
82 }
6d37c2c9 83 i_plin(im, dest_x, dest_x+width, dest_y + y, line);
ece126f9
TC
84 }
85 myfree(line);
86 result = 1;
87 }
88 else {
c50a933e 89 i_push_win32_errorf(GetLastError(), "GetDIBits() failure: ");
ece126f9
TC
90 }
91
92 myfree(di_bits);
93 SelectObject(bmdc, old_dc_bmp);
94 DeleteDC(bmdc);
95 DeleteObject(work_bmp);
96
97 return result;
98}
99
6d37c2c9
TC
100static BOOL CALLBACK
101monitor_enum(HMONITOR hmon, HDC dc, LPRECT r, LPARAM lp_ctx) {
102 struct monitor_ctx *ctx = (struct monitor_ctx *)lp_ctx;
103
104 if (!display_to_img(dc, ctx->out, r,
105 r->left - ctx->orig_x, r->top - ctx->orig_y)) {
106 ctx->good = 0;
107 }
108
109 return ctx->good;
110}
111
0ddb7051 112i_img *
87cd516f 113imss_win32(unsigned hwnd_u, int include_decor, int left, int top,
4e6ce56a 114 int right, int bottom, int display) {
0ddb7051 115 HWND hwnd = (HWND)hwnd_u;
ece126f9 116 HDC cdc = 0, wdc;
4e6ce56a
TC
117 int orig_x = 0;
118 int orig_y = 0;
87cd516f 119 int window_width, window_height;
0ddb7051 120 i_img *result = NULL;
87cd516f 121 int width, height;
ece126f9 122 int channels = 3;
0ddb7051
TC
123
124 i_clear_error();
125
4e6ce56a
TC
126 if (hwnd) {
127 RECT rect;
128 if (include_decor) {
129 wdc = GetWindowDC(hwnd);
130 GetWindowRect(hwnd, &rect);
131 }
132 else {
133 wdc = GetDC(hwnd);
134 GetClientRect(hwnd, &rect);
135 }
136 if (!wdc) {
137 i_push_error(0, "Cannot get window DC - invalid hwnd?");
138 return NULL;
139 }
0ddb7051 140
4e6ce56a
TC
141 window_width = rect.right - rect.left;
142 window_height = rect.bottom - rect.top;
0ddb7051
TC
143 }
144 else {
4e6ce56a 145 if (display == -1) {
4e6ce56a
TC
146 cdc = CreateDC("DISPLAY", NULL, NULL, NULL);
147 orig_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
148 orig_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
149 window_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
150 window_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
ece126f9 151 channels = 4;
4e6ce56a
TC
152 }
153 else {
154 DISPLAY_DEVICE dd;
c50a933e
TC
155 int work_display = 0;
156 int primary_display = -1;
157 int real_display = -1;
158
6a912b63 159 dd.cb = sizeof(dd);
c50a933e
TC
160 /* look for the primary display, we need a simulate a gap to put the
161 primary at 0 */
162 while (work_display < 100
163 && EnumDisplayDevices(NULL, work_display, &dd, 0)) {
164 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
165 primary_display = work_display;
166 break;
f7119376 167 }
c50a933e
TC
168 else if (display && display-1 == work_display) {
169 real_display = work_display;
170 break;
171 }
172
173 dd.cb = sizeof(dd);
174 ++work_display;
175 }
176
177 if (!work_display && real_display == -1 && primary_display == -1) {
178 /* EnumDisplayDevices() failed for the first call */
179 i_push_win32_errorf(GetLastError(), "Cannot enumerate device %d(0): ", work_display);
180 return NULL;
181 }
182
183 if (primary_display != -1 && display == 0) {
184 real_display = primary_display;
185 }
186
187 if (real_display == -1) {
188 /* haven't enumerated the display we want yet */
189 /* we're after the primary */
190 real_display = display;
191 dd.cb = sizeof(dd);
192 if (!EnumDisplayDevices(NULL, real_display, &dd, 0)) {
193 i_push_win32_errorf(GetLastError(), "Cannot enumerate device %d(%d): ",
194 real_display, display);
f7119376
TC
195 return NULL;
196 }
4e6ce56a 197 }
c50a933e
TC
198
199 if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
200 i_push_errorf(0, "Display device %d not active", display);
201 return NULL;
202 }
203 cdc = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);
204 if (!cdc) {
205 i_push_win32_errorf(GetLastError(), "Cannot CreateDC(%s): ", dd.DeviceName);
4e6ce56a
TC
206 return NULL;
207 }
208
209 window_width = GetDeviceCaps(cdc, HORZRES);
210 window_height = GetDeviceCaps(cdc, VERTRES);
211 }
212
213 wdc = cdc;
214 }
87cd516f
TC
215
216 /* adjust negative/zero values to window size */
217 if (left < 0)
218 left += window_width;
219 if (top < 0)
220 top += window_height;
221 if (right <= 0)
222 right += window_width;
223 if (bottom <= 0)
224 bottom += window_height;
225
226 /* clamp */
227 if (left < 0)
228 left = 0;
229 if (right > window_width)
230 right = window_width;
231 if (top < 0)
232 top = 0;
233 if (bottom > window_height)
234 bottom = window_height;
235
236 /* validate */
237 if (right <= left || bottom <= top) {
238 i_push_error(0, "image would be empty");
f7119376
TC
239 if (cdc)
240 DeleteDC(cdc);
241 else
242 ReleaseDC(hwnd, wdc);
87cd516f
TC
243 return NULL;
244 }
245 width = right - left;
246 height = bottom - top;
ece126f9
TC
247
248 result = i_img_8_new(width, height, channels);
249
250 if (result) {
251 RECT r;
252 r.left = orig_x + left;
253 r.top = orig_y + top;
254 r.right = r.left + width;
255 r.bottom = r.top + height;
256
6d37c2c9
TC
257 if (display == -1) {
258 struct monitor_ctx ctx;
259 ctx.out = result;
260 ctx.orig_x = orig_x;
261 ctx.orig_y = orig_y;
262 ctx.good = 1;
263
264 if (!EnumDisplayMonitors(wdc, &r, monitor_enum, (LPARAM)&ctx)
265 || !ctx.good) {
266 i_img_destroy(result);
267 result = NULL;
268 }
269 }
270 else {
271 if (!display_to_img(wdc, result, &r, 0, 0)) {
272 i_img_destroy(result);
273 result = NULL;
274 }
275 }
276 if (result) {
ece126f9
TC
277 i_tags_setn(&result->tags, "ss_window_width", window_width);
278 i_tags_setn(&result->tags, "ss_window_height", window_height);
279 i_tags_set(&result->tags, "ss_type", "Win32", 5);
280 i_tags_setn(&result->tags, "ss_left", left);
281 i_tags_setn(&result->tags, "ss_top", top);
282 }
0ddb7051 283 }
0ddb7051 284 /* clean up */
4e6ce56a
TC
285 if (cdc) {
286 DeleteDC(cdc);
287 }
288 else {
289 ReleaseDC(hwnd, wdc);
290 }
0ddb7051
TC
291
292 return result;
293}