Commit | Line | Data |
---|---|---|
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 |
11 | static void |
12 | i_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 | 36 | struct monitor_ctx { |
ece126f9 TC |
37 | i_img *out; |
38 | i_img_dim orig_x, orig_y; | |
6d37c2c9 | 39 | int good; |
ece126f9 TC |
40 | }; |
41 | ||
42 | static int | |
43 | display_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 |
100 | static BOOL CALLBACK |
101 | monitor_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 | 112 | i_img * |
87cd516f | 113 | imss_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 | } |