1 #include "main.h"
2 
3 #include "../debug.h"
4 #include "../flist.h"
5 #include "../friend.h"
6 #include "../stb.h"
7 #include "../tox.h"
8 
9 #include "../av/utox_av.h"
10 
11 #include <windowsx.h>
12 
13 static HWND      grab_window;
14 static HINSTANCE grab_instance;
15 static bool desktopgrab_video = false;
16 
17 // creates an UTOX_NATIVE image based on given arguments
18 // image should be freed with image_free
create_utox_image(HBITMAP bmp,bool has_alpha,uint32_t width,uint32_t height)19 static NATIVE_IMAGE *create_utox_image(HBITMAP bmp, bool has_alpha, uint32_t width, uint32_t height) {
20     NATIVE_IMAGE *image = malloc(sizeof(NATIVE_IMAGE));
21     if (!image) {
22         LOG_ERR("NATIVE Screengrab", "create_utox_image:\t Could not allocate memory for image.");
23         return NULL;
24     }
25 
26     image->bitmap        = bmp;
27     image->has_alpha     = has_alpha;
28     image->width         = width;
29     image->height        = height;
30     image->scaled_width  = width;
31     image->scaled_height = height;
32     image->stretch_mode  = COLORONCOLOR;
33 
34     return image;
35 }
36 
sendbitmap(HDC mem,HBITMAP hbm,int width,int height)37 static void sendbitmap(HDC mem, HBITMAP hbm, int width, int height) {
38     if (width == 0 || height == 0) {
39         return;
40     }
41 
42     BITMAPINFO info = {
43         .bmiHeader = {
44             .biSize        = sizeof(BITMAPINFOHEADER),
45             .biWidth       = width,
46             .biHeight      = -height,
47             .biPlanes      = 1,
48             .biBitCount    = 24,
49             .biCompression = BI_RGB,
50         }
51     };
52 
53     void *bits = malloc((width + 3) * height * 3);
54 
55     GetDIBits(mem, hbm, 0, height, bits, &info, DIB_RGB_COLORS);
56 
57     uint8_t pbytes = width & 3, *p = bits, *pp = bits, *end = p + width * height * 3;
58     // uint32_t offset = 0;
59     while (p != end) {
60         int i;
61         for (i = 0; i != width; i++) {
62             uint8_t b    = pp[i * 3];
63             p[i * 3]     = pp[i * 3 + 2];
64             p[i * 3 + 1] = pp[i * 3 + 1];
65             p[i * 3 + 2] = b;
66         }
67         p += width * 3;
68         pp += width * 3 + pbytes;
69     }
70 
71     int size = 0;
72     UTOX_IMAGE out = stbi_write_png_to_mem(bits, 0, width, height, 3, &size);
73 
74     free(bits);
75 
76     NATIVE_IMAGE *image = create_utox_image(hbm, 0, width, height);
77     friend_sendimage(flist_get_friend(), image, width, height, out, size);
78 }
79 
screen_grab_sys(HWND window,UINT msg,WPARAM wParam,LPARAM lParam)80 static LRESULT CALLBACK screen_grab_sys(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
81     POINT p = {.x = GET_X_LPARAM(lParam), .y = GET_Y_LPARAM(lParam) };
82 
83     ClientToScreen(window, &p);
84 
85     static bool mdown = false;
86     switch (msg) {
87         case WM_MOUSEMOVE: {
88             if (!mdown) {
89                 break;
90             }
91 
92             HDC dc = GetDC(window);
93             BitBlt(dc, video_grab_x, video_grab_y, video_grab_w - video_grab_x, video_grab_h - video_grab_y, dc,
94                        video_grab_x, video_grab_y, BLACKNESS);
95             video_grab_w = p.x;
96             video_grab_h = p.y;
97             BitBlt(dc, video_grab_x, video_grab_y, video_grab_w - video_grab_x, video_grab_h - video_grab_y, dc,
98                        video_grab_x, video_grab_y, WHITENESS);
99             ReleaseDC(window, dc);
100             return false;
101         }
102 
103         case WM_LBUTTONDOWN: {
104             mdown = true;
105             video_grab_x = video_grab_w = p.x;
106             video_grab_y = video_grab_h = p.y;
107             SetCapture(window);
108             return false;
109         }
110 
111         case WM_LBUTTONUP: {
112             mdown = false;
113             ReleaseCapture();
114 
115             if (video_grab_x < video_grab_w) {
116                 video_grab_w -= video_grab_x;
117             } else {
118                 const int w  = video_grab_x - video_grab_w;
119                 video_grab_x = video_grab_w;
120                 video_grab_w = w;
121             }
122 
123             if (video_grab_y < video_grab_h) {
124                 video_grab_h -= video_grab_y;
125             } else {
126                 const int h  = video_grab_y - video_grab_h;
127                 video_grab_y = video_grab_h;
128                 video_grab_h = h;
129             }
130 
131             if (desktopgrab_video) {
132                 DestroyWindow(window);
133                 postmessage_utoxav(UTOXAV_SET_VIDEO_IN, 1, 0, NULL);
134             } else {
135                 if (flist_get_friend()) {
136                     FRIEND *f = flist_get_friend();
137                     if (f->online) {
138                         DestroyWindow(window);
139                         HWND dwnd = GetDesktopWindow();
140                         HDC  ddc  = GetDC(dwnd);
141                         HDC  mem  = CreateCompatibleDC(ddc);
142 
143                         HBITMAP capture = CreateCompatibleBitmap(ddc, video_grab_w, video_grab_h);
144                         SelectObject(mem, capture);
145 
146                         BitBlt(mem, 0, 0, video_grab_w, video_grab_h, ddc, video_grab_x, video_grab_y, SRCCOPY | CAPTUREBLT);
147                         sendbitmap(mem, capture, video_grab_w, video_grab_h);
148 
149                         ReleaseDC(dwnd, ddc);
150                         DeleteDC(mem);
151                     }
152                 }
153             }
154             return false;
155         }
156     }
157 
158     return DefWindowProcW(window, msg, wParam, lParam);
159 }
160 
screen_grab_init(HINSTANCE app_instance)161 void screen_grab_init(HINSTANCE app_instance) {
162     HICON screengrab_black_icon  = LoadIcon(app_instance, MAKEINTRESOURCE(101));
163     wchar_t screen_grab_class[] = L"uToxgrab";
164 
165     WNDCLASSW grab_window_class = {
166         .hInstance     = app_instance,
167         .lpfnWndProc   = screen_grab_sys,
168         .lpszClassName = screen_grab_class,
169         .hIcon         = screengrab_black_icon,
170         .hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH),
171     };
172     RegisterClassW(&grab_window_class);
173 
174     grab_instance = app_instance;
175 }
176 
native_screen_grab_desktop(bool video)177 void native_screen_grab_desktop(bool video) {
178     int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
179     int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
180     int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
181     int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
182 
183     LOG_TRACE("Native Screengrab", "result: %i %i %i %i" , x, y, w, h);
184 
185     grab_window = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_LAYERED, L"uToxgrab", L"Tox", WS_POPUP,
186                                  x, y, w, h,
187                                  NULL, NULL, grab_instance, NULL);
188     if (!grab_window) {
189         LOG_TRACE("Native Screengrab", "CreateWindowExW() failed" );
190         return;
191     }
192 
193     SetLayeredWindowAttributes(grab_window, 0xFFFFFF, 128, LWA_ALPHA | LWA_COLORKEY);
194     // UpdateLayeredWindow(main_window.window, NULL, NULL, NULL, NULL, NULL, 0xFFFFFF, ULW_ALPHA | ULW_COLORKEY);
195 
196     ShowWindow(grab_window, SW_SHOW);
197     SetForegroundWindow(grab_window);
198 
199     desktopgrab_video = video;
200 }
201