make sure GetDIBits() failures are reported
[imager-screenshot.git] / scwin32.c
1 #include "imext.h"
2 #include <windows.h>
3 #include <string.h>
4 #include "imss.h"
5
6 i_img *
7 imss_win32(unsigned hwnd_u, int include_decor, int left, int top, 
8            int right, int bottom, int display) {
9   HWND hwnd = (HWND)hwnd_u;
10   HDC cdc = 0, wdc, bmdc;
11   HBITMAP work_bmp, old_dc_bmp;
12   int orig_x = 0;
13   int orig_y = 0;
14   int window_width, window_height;
15   BITMAPINFO bmi;
16   unsigned char *di_bits;
17   i_img *result = NULL;
18   int width, height;
19
20   i_clear_error();
21
22   if (hwnd) {
23     RECT rect;
24     if (include_decor) {
25       wdc = GetWindowDC(hwnd);
26       GetWindowRect(hwnd, &rect);
27     }
28     else {
29       wdc = GetDC(hwnd);
30       GetClientRect(hwnd, &rect);
31     }
32     if (!wdc) {
33       i_push_error(0, "Cannot get window DC - invalid hwnd?");
34       return NULL;
35     }
36
37     window_width = rect.right - rect.left;
38     window_height = rect.bottom - rect.top;
39   }
40   else {
41     if (display == -1) {
42       cdc = CreateDC("DISPLAY", NULL, NULL, NULL);
43       orig_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
44       orig_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
45       window_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
46       window_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
47     }
48     else {
49       DISPLAY_DEVICE dd;
50       dd.cb = sizeof(dd);
51
52       if (EnumDisplayDevices(NULL, display, &dd, 0)) {
53         if (dd.StateFlags & DISPLAY_DEVICE_ACTIVE) {
54           cdc = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);
55         }
56         else {
57           i_push_errorf(0, "Display device %d not active", display);
58           return NULL;
59         }
60       }
61       else {
62         i_push_errorf(0, "Cannot enumerate device %d: %ld", display, (long)GetLastError());
63         return NULL;
64       }
65
66       window_width = GetDeviceCaps(cdc, HORZRES);
67       window_height = GetDeviceCaps(cdc, VERTRES);
68     }
69
70     wdc = cdc;
71   }
72
73   /* adjust negative/zero values to window size */
74   if (left < 0)
75     left += window_width;
76   if (top < 0)
77     top += window_height;
78   if (right <= 0)
79     right += window_width;
80   if (bottom <= 0)
81     bottom += window_height;
82   
83   /* clamp */
84   if (left < 0)
85     left = 0;
86   if (right > window_width)
87     right = window_width;
88   if (top < 0)
89     top = 0;
90   if (bottom > window_height)
91     bottom = window_height;
92
93   /* validate */
94   if (right <= left || bottom <= top) {
95     i_push_error(0, "image would be empty");
96     if (cdc)
97       DeleteDC(cdc);
98     else
99       ReleaseDC(hwnd, wdc);
100     return NULL;
101   }
102   width = right - left;
103   height = bottom - top;
104
105   work_bmp = CreateCompatibleBitmap(wdc, width, height);
106   bmdc = CreateCompatibleDC(wdc);
107   old_dc_bmp = SelectObject(bmdc, work_bmp);
108   BitBlt(bmdc, 0, 0, width, height, wdc, orig_x + left, orig_y + top, SRCCOPY);
109
110   /* make a dib */
111   memset(&bmi, 0, sizeof(bmi));
112   bmi.bmiHeader.biSize = sizeof(bmi);
113   bmi.bmiHeader.biWidth = width;
114   bmi.bmiHeader.biHeight = -height;
115   bmi.bmiHeader.biPlanes = 1;
116   bmi.bmiHeader.biBitCount = 32;
117   bmi.bmiHeader.biCompression = BI_RGB;
118
119   di_bits = mymalloc(4 * width * height);
120   if (GetDIBits(bmdc, work_bmp, 0, height, di_bits, &bmi, DIB_RGB_COLORS)) {
121     result = i_img_8_new(width, height, 3);
122
123     if (result) {
124       i_color *line = mymalloc(sizeof(i_color) * width);
125       i_color *cp;
126       int x, y;
127       unsigned char *ch_pp = di_bits;
128       for (y = 0; y < height; ++y) {
129         cp = line;
130         for (x = 0; x < width; ++x) {
131           cp->rgb.b = *ch_pp++;
132           cp->rgb.g = *ch_pp++;
133           cp->rgb.r = *ch_pp++;
134           ch_pp++;
135           cp++;
136         }
137         i_plin(result, 0, width, y, line);
138       }
139       myfree(line);
140     }
141   }
142   else {
143     i_push_errorf(0, "GetDIBits() failure %d", (long)GetLastError());
144   }
145
146   i_tags_setn(&result->tags, "ss_window_width", window_width);
147   i_tags_setn(&result->tags, "ss_window_height", window_height);
148   i_tags_set(&result->tags, "ss_type", "Win32", 5);
149   i_tags_setn(&result->tags, "ss_left", left);
150   i_tags_setn(&result->tags, "ss_top", top);
151
152   /* clean up */
153   myfree(di_bits);
154   SelectObject(bmdc, old_dc_bmp);
155   DeleteDC(bmdc);
156   DeleteObject(work_bmp);
157
158   if (cdc) {
159     DeleteDC(cdc);
160   }
161   else {
162     ReleaseDC(hwnd, wdc);
163   }
164
165   return result;
166 }