1 #include "events.h"
2
3 #include "main.h"
4 #include "window.h"
5
6 #include "../commands.h"
7 #include "../debug.h"
8 #include "../flist.h"
9 #include "../friend.h"
10 #include "../macros.h"
11 #include "../self.h"
12 #include "../settings.h"
13 #include "../theme.h"
14 #include "../tox.h"
15 #include "../utox.h"
16
17 #include "../av/utox_av.h"
18
19 #include "../native/clipboard.h"
20 #include "../native/keyboard.h"
21 #include "../native/notify.h"
22 #include "../native/ui.h"
23
24 #include "../ui/dropdown.h"
25 #include "../ui/edit.h"
26 #include "../ui/svg.h"
27
28 #include "../layout/background.h"
29 #include "../layout/notify.h"
30 #include "../layout/settings.h"
31
32 #include <windowsx.h>
33
34 #include "../main.h" // main_width
35
36 static TRACKMOUSEEVENT tme = {
37 sizeof(TRACKMOUSEEVENT),
38 TME_LEAVE,
39 0,
40 0,
41 };
42
43 static bool mouse_tracked = false;
44
45 /** Toggles the main window to/from hidden to tray/shown. */
togglehide(int show)46 static void togglehide(int show) {
47 if (hidden || show) {
48 ShowWindow(main_window.window, SW_RESTORE);
49 SetForegroundWindow(main_window.window);
50 redraw();
51 hidden = false;
52 } else {
53 ShowWindow(main_window.window, SW_HIDE);
54 hidden = true;
55 }
56 }
57
58 /** Right click context menu for the tray icon */
ShowContextMenu(void)59 static void ShowContextMenu(void) {
60 POINT pt;
61 GetCursorPos(&pt);
62 HMENU hMenu = CreatePopupMenu();
63 if (hMenu) {
64 InsertMenu(hMenu, -1, MF_BYPOSITION, TRAY_SHOWHIDE, hidden ? "Restore" : "Hide");
65
66 InsertMenu(hMenu, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
67
68 InsertMenu(hMenu, -1, MF_BYPOSITION | ((self.status == TOX_USER_STATUS_NONE) ? MF_CHECKED : 0),
69 TRAY_STATUS_AVAILABLE, "Available");
70 InsertMenu(hMenu, -1, MF_BYPOSITION | ((self.status == TOX_USER_STATUS_AWAY) ? MF_CHECKED : 0),
71 TRAY_STATUS_AWAY, "Away");
72 InsertMenu(hMenu, -1, MF_BYPOSITION | ((self.status == TOX_USER_STATUS_BUSY) ? MF_CHECKED : 0),
73 TRAY_STATUS_BUSY, "Busy");
74
75 InsertMenu(hMenu, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
76
77 InsertMenu(hMenu, -1, MF_BYPOSITION, TRAY_EXIT, "Exit");
78
79 // note: must set window to the foreground or the
80 // menu won't disappear when it should
81 SetForegroundWindow(main_window.window);
82
83 TrackPopupMenu(hMenu, TPM_BOTTOMALIGN, pt.x, pt.y, 0, main_window.window, NULL);
84 DestroyMenu(hMenu);
85 }
86 }
87
88 /* TODO should this be moved to window.c? */
move_window(int x,int y)89 static void move_window(int x, int y){
90 LOG_TRACE("Win events", "delta x == %i\n", x);
91 LOG_TRACE("Win events", "delta y == %i\n", y);
92 SetWindowPos(main_window.window, 0, main_window._.x + x, main_window._.y + y, 0, 0,
93 SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW);
94 main_window._.x += x;
95 main_window._.y += y;
96 }
97
98 #define setstatus(x) \
99 if (self.status != x) { \
100 postmessage_toxcore(TOX_SELF_SET_STATE, x, 0, NULL); \
101 self.status = x; \
102 redraw(); \
103 }
104
105 /** Handles all callback requests from winmain();
106 *
107 * handles the window functions internally, and ships off the tox calls to tox
108 */
WindowProc(HWND window,UINT msg,WPARAM wParam,LPARAM lParam)109 LRESULT CALLBACK WindowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
110 static int mx, my;
111 static bool mdown = false;
112 static int mdown_x, mdown_y;
113 static uint32_t taskbar_created;
114
115 if (main_window.window && window != main_window.window) {
116 if (msg == WM_DESTROY) {
117 if (window == preview_hwnd) {
118 if (settings.video_preview) {
119 settings.video_preview = false;
120 postmessage_utoxav(UTOXAV_STOP_VIDEO, UINT16_MAX, 0, NULL);
121 }
122
123 return false;
124 }
125
126 for (uint8_t i = 0; i < self.friend_list_count; i++) {
127 if (video_hwnd[i] == window) {
128 FRIEND *f = get_friend(i);
129 postmessage_utoxav(UTOXAV_STOP_VIDEO, f->number, 0, NULL);
130 break;
131 }
132 }
133 }
134
135 LOG_TRACE("WinEvent", "Uncaught event %u & %u", wParam, lParam);
136 return DefWindowProcW(window, msg, wParam, lParam);
137 }
138
139 switch (msg) {
140 case WM_QUIT:
141 case WM_CLOSE:
142 case WM_DESTROY: {
143 if (settings.close_to_tray) {
144 LOG_INFO("Events", "Closing to tray." );
145 togglehide(0);
146 return true;
147 } else {
148 PostQuitMessage(0);
149 return false;
150 }
151 }
152
153 case WM_GETMINMAXINFO: {
154 POINT min = { SCALE(MAIN_WIDTH), SCALE(MAIN_HEIGHT) };
155 ((MINMAXINFO *)lParam)->ptMinTrackSize = min;
156
157 break;
158 }
159
160 case WM_CREATE: {
161 LOG_INFO("Windows", "WM_CREATE");
162 taskbar_created = RegisterWindowMessage(TEXT("TaskbarCreated"));
163 return false;
164 }
165
166 case WM_SIZE: {
167 switch (wParam) {
168 case SIZE_MAXIMIZED: {
169 settings.window_maximized = true;
170 break;
171 }
172
173 case SIZE_RESTORED: {
174 settings.window_maximized = false;
175 break;
176 }
177 }
178
179 int w = GET_X_LPARAM(lParam);
180 int h = GET_Y_LPARAM(lParam);
181
182 if (w != 0) {
183 RECT r;
184 GetClientRect(window, &r);
185 w = r.right;
186 h = r.bottom;
187
188 settings.window_width = w;
189 settings.window_height = h;
190
191 ui_rescale(dropdown_dpi.selected + 5);
192 ui_size(w, h);
193
194 if (main_window.draw_BM) {
195 DeleteObject(main_window.draw_BM);
196 }
197
198 main_window.draw_BM = CreateCompatibleBitmap(main_window.window_DC, settings.window_width,
199 settings.window_height);
200 SelectObject(main_window.window_DC, main_window.draw_BM);
201 redraw();
202 }
203 break;
204 }
205
206 case WM_SETFOCUS: {
207 if (flashing) {
208 FlashWindow(main_window.window, false);
209 flashing = false;
210
211 NOTIFYICONDATAW nid = {
212 .uFlags = NIF_ICON,
213 .hWnd = main_window.window,
214 .hIcon = black_icon,
215 .cbSize = sizeof(nid),
216 };
217
218 Shell_NotifyIconW(NIM_MODIFY, &nid);
219 }
220
221 have_focus = true;
222 break;
223 }
224
225 case WM_KILLFOCUS: {
226 have_focus = false;
227 break;
228 }
229
230 case WM_ERASEBKGND: {
231 return true;
232 }
233
234 case WM_PAINT: {
235 PAINTSTRUCT ps;
236
237 BeginPaint(window, &ps);
238
239 RECT r = ps.rcPaint;
240 BitBlt(main_window.window_DC, r.left, r.top, r.right - r.left, r.bottom - r.top,
241 main_window.draw_DC, r.left, r.top, SRCCOPY);
242
243 EndPaint(window, &ps);
244 return false;
245 }
246
247 case WM_SYSKEYDOWN: // called instead of WM_KEYDOWN when ALT is down or F10 is pressed
248 case WM_KEYDOWN: {
249 bool control = (GetKeyState(VK_CONTROL) & 0x80) != 0;
250 bool shift = (GetKeyState(VK_SHIFT) & 0x80) != 0;
251 bool alt = (GetKeyState(VK_MENU) & 0x80) != 0; /* Be careful not to clobber alt+num symbols */
252
253 if (wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9) {
254 // normalize keypad and non-keypad numbers
255 wParam = wParam - VK_NUMPAD0 + '0';
256 }
257
258 if (control && wParam == 'C') {
259 copy(1);
260 return false;
261 }
262
263 if (control) {
264 if ((wParam == VK_TAB && shift) || wParam == VK_PRIOR) {
265 flist_previous_tab();
266 redraw();
267 return false;
268 } else if (wParam == VK_TAB || wParam == VK_NEXT) {
269 flist_next_tab();
270 redraw();
271 return false;
272 }
273 }
274
275 if (control && !alt) {
276 if (wParam >= '1' && wParam <= '9') {
277 flist_selectchat(wParam - '1');
278 redraw();
279 return false;
280 } else if (wParam == '0') {
281 flist_selectchat(9);
282 redraw();
283 return false;
284 }
285 }
286
287 if (edit_active()) {
288 if (control) {
289 switch (wParam) {
290 case 'V':
291 paste();
292 return false;
293 case 'X':
294 copy(0);
295 edit_char(KEY_DEL, 1, 0);
296 return false;
297 }
298 }
299
300 if (control || ((wParam < 'A' || wParam > 'Z') && wParam != VK_RETURN && wParam != VK_BACK)) {
301 edit_char(wParam, 1, (control << 2) | shift);
302 }
303 } else {
304 messages_char(wParam);
305 redraw(); // TODO maybe if this
306 break;
307 }
308
309 break;
310 }
311
312 case WM_CHAR: {
313 if (edit_active()) {
314 if (wParam == KEY_RETURN && (GetKeyState(VK_SHIFT) & 0x80)) {
315 wParam = '\n';
316 }
317
318 if (wParam != KEY_TAB) {
319 edit_char(wParam, 0, 0);
320 }
321 }
322
323 return false;
324 }
325
326 case WM_MOUSEWHEEL: {
327 double delta = (double)GET_WHEEL_DELTA_WPARAM(wParam);
328 mx = GET_X_LPARAM(lParam);
329 my = GET_Y_LPARAM(lParam);
330
331 panel_mwheel(&panel_root, mx, my, settings.window_width, settings.window_height,
332 delta / (double)(WHEEL_DELTA), 1);
333 return false;
334 }
335
336 case WM_MOUSEMOVE: {
337 int x, y, dx, dy;
338
339 x = GET_X_LPARAM(lParam);
340 y = GET_Y_LPARAM(lParam);
341
342 dx = x - mx;
343 dy = y - my;
344 mx = x;
345 my = y;
346
347
348 if (btn_move_window_down) {
349 move_window(x - mdown_x, y - mdown_y);
350 }
351
352 cursor = 0;
353 panel_mmove(&panel_root, 0, 0, settings.window_width, settings.window_height, x, y, dx, dy);
354
355 SetCursor(cursors[cursor]);
356
357 if (!mouse_tracked) {
358 TrackMouseEvent(&tme);
359 mouse_tracked = true;
360 }
361
362 return false;
363 }
364
365 case WM_LBUTTONDOWN: {
366 mdown_x = GET_X_LPARAM(lParam);
367 mdown_y = GET_Y_LPARAM(lParam);
368 // Intentional fall through to save the original mdown location.
369 }
370 case WM_LBUTTONDBLCLK: {
371 mdown = true;
372
373 int x = GET_X_LPARAM(lParam);
374 int y = GET_Y_LPARAM(lParam);
375
376 if (x != mx || y != my) {
377 panel_mmove(&panel_root, 0, 0, settings.window_width, settings.window_height, x, y, x - mx, y - my);
378 mx = x;
379 my = y;
380 }
381
382 // double redraw>
383 panel_mdown(&panel_root);
384 if (msg == WM_LBUTTONDBLCLK) {
385 panel_dclick(&panel_root, 0);
386 }
387
388 SetCapture(window);
389 break;
390 }
391
392 case WM_RBUTTONDOWN: {
393 panel_mright(&panel_root);
394 break;
395 }
396
397 case WM_RBUTTONUP: {
398 break;
399 }
400
401 case WM_LBUTTONUP: {
402 ReleaseCapture();
403 break;
404 }
405
406 case WM_CAPTURECHANGED: {
407 if (mdown) {
408 panel_mup(&panel_root);
409 mdown = false;
410 }
411
412 break;
413 }
414
415 case WM_MOUSELEAVE: {
416 ui_mouseleave();
417 mouse_tracked = false;
418 btn_move_window_down = false;
419 LOG_TRACE("Win events", "mouse leave\n");
420 break;
421 }
422
423
424 case WM_COMMAND: {
425 int menu = LOWORD(wParam); //, msg = HIWORD(wParam);
426
427 switch (menu) {
428 case TRAY_SHOWHIDE: {
429 togglehide(0);
430 break;
431 }
432
433 case TRAY_EXIT: {
434 PostQuitMessage(0);
435 break;
436 }
437
438 case TRAY_STATUS_AVAILABLE: {
439 setstatus(TOX_USER_STATUS_NONE);
440 break;
441 }
442
443 case TRAY_STATUS_AWAY: {
444 setstatus(TOX_USER_STATUS_AWAY);
445 break;
446 }
447
448 case TRAY_STATUS_BUSY: {
449 setstatus(TOX_USER_STATUS_BUSY);
450 break;
451 }
452 }
453
454 break;
455 }
456
457 case WM_NOTIFYICON: {
458 int message = LOWORD(lParam);
459
460 switch (message) {
461 case WM_MOUSEMOVE: {
462 break;
463 }
464
465 case WM_LBUTTONDOWN: {
466 togglehide(0);
467 break;
468 }
469
470 case WM_LBUTTONDBLCLK: {
471 togglehide(1);
472 break;
473 }
474
475 case WM_LBUTTONUP: {
476 break;
477 }
478
479 case WM_RBUTTONDOWN: {
480 break;
481 }
482
483 case WM_RBUTTONUP:
484 case WM_CONTEXTMENU: {
485 ShowContextMenu();
486 break;
487 }
488 }
489
490 return false;
491 }
492
493 case WM_COPYDATA: {
494 togglehide(1);
495 SetForegroundWindow(window);
496 COPYDATASTRUCT *data = (void *)lParam;
497 if (data->lpData) {
498 do_tox_url(data->lpData, data->cbData);
499 }
500
501 return false;
502 }
503
504 case WM_TOX ... WM_TOX + 128: {
505 utox_message_dispatch(msg - WM_TOX, wParam >> 16, wParam, (void *)lParam);
506 return false;
507 }
508
509 default: {
510 if (msg == taskbar_created) {
511 tray_icon_init(main_window.window, black_icon);
512 }
513 break;
514 }
515 }
516
517 return DefWindowProcW(window, msg, wParam, lParam);
518 }
519