OpenClonk
C4WindowWin32.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* A wrapper class to OS dependent event and window interfaces, WIN32 version */
19 
20 #include "C4Include.h"
22 #include "platform/C4Window.h"
23 
24 #include "C4Version.h"
25 #include "editor/C4Console.h"
27 #include "game/C4Application.h"
28 #include "game/C4FullScreen.h"
29 #include "game/C4GraphicsSystem.h"
30 #include "game/C4Viewport.h"
31 #include "graphics/C4DrawGL.h"
32 #include "gui/C4MouseControl.h"
33 #include "lib/C4Rect.h"
35 #include "platform/StdRegistry.h"
36 #include "res/resource.h"
37 
39 #include <mmsystem.h>
40 #include <shellapi.h>
41 
42 #define C4ViewportClassName L"C4Viewport"
43 #define C4FullScreenClassName L"C4FullScreen"
44 #define ConsoleDlgClassName L"C4GUIdlg"
45 #define ConsoleDlgWindowStyle (WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX)
46 
48 static void ConvertToUnixScancode(WPARAM wParam, C4KeyCode *scancode, bool extended)
49 {
50  C4KeyCode &s = *scancode;
51 
52  switch(wParam)
53  {
54  case VK_HOME: s = K_HOME; break;
55  case VK_END: s = K_END; break;
56  case VK_PRIOR: s = K_PAGEUP; break;
57  case VK_NEXT: s = K_PAGEDOWN; break;
58  case VK_UP: s = K_UP; break;
59  case VK_DOWN: s = K_DOWN; break;
60  case VK_LEFT: s = K_LEFT; break;
61  case VK_RIGHT: s = K_RIGHT; break;
62  case VK_CLEAR: s = K_CENTER; break;
63  case VK_INSERT: s = K_INSERT; break;
64  case VK_DELETE: s = K_DELETE; break;
65  case VK_LWIN: s = K_WIN_L; break;
66  case VK_RWIN: s = K_WIN_R; break;
67  case VK_MENU: s = K_MENU; break;
68  case VK_PAUSE: s = K_PAUSE; break;
69  case VK_PRINT: s = K_PRINT; break;
70  case VK_RCONTROL: s = K_CONTROL_R; break;
71  case VK_CONTROL: s = (extended ? K_CONTROL_R : K_CONTROL_L); break;
72  case VK_NUMLOCK: s = K_NUM; break;
73  case VK_NUMPAD1: s = K_NUM1; break;
74  case VK_NUMPAD2: s = K_NUM2; break;
75  case VK_NUMPAD3: s = K_NUM3; break;
76  case VK_NUMPAD4: s = K_NUM4; break;
77  case VK_NUMPAD5: s = K_NUM5; break;
78  case VK_NUMPAD6: s = K_NUM6; break;
79  case VK_NUMPAD7: s = K_NUM7; break;
80  case VK_NUMPAD8: s = K_NUM8; break;
81  case VK_NUMPAD9: s = K_NUM9; break;
82  case VK_NUMPAD0: s = K_NUM0; break;
83  }
84 }
85 
86 LRESULT APIENTRY FullScreenWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
87 {
88  static bool NativeCursorShown = true;
89 
90  POINT p;
91  p.x = GET_X_LPARAM(lParam);
92  p.y = GET_Y_LPARAM(lParam);
93 
94  // compute scancode
95  C4KeyCode scancode = (((unsigned int)lParam) >> 16) & 0xFF;
96  bool extended = ((lParam & 0x01000000) != 0);
97  ConvertToUnixScancode(wParam, &scancode, extended);
98 
99  // Process message
100  switch (uMsg)
101  {
102  case WM_ACTIVATE:
103  wParam = (LOWORD(wParam)==WA_ACTIVE || LOWORD(wParam)==WA_CLICKACTIVE);
104  // fall through to next case
105  case WM_ACTIVATEAPP:
106  Application.Active = wParam != 0;
107 #ifndef USE_CONSOLE
108  if (pGL)
109  {
110  if (Application.Active)
111  {
112  // restore textures
114  {
116  }
117  }
118  else
119  {
121  {
122  ::ChangeDisplaySettings(nullptr, 0);
123  ::ShowWindow(hwnd, SW_MINIMIZE);
124  }
125  }
126  }
127 #endif
128  // redraw background
130  // Redraw after task switch
131  if (Application.Active)
133  // update cursor clip
135  return false;
136  case WM_PAINT:
137  // Redraw after task switch
138  if (Application.Active)
140  break;
141  case WM_DESTROY:
142  Application.Quit();
143  return 0;
144  case WM_CLOSE:
145  FullScreen.Close();
146  return 0;
147  case MM_MCINOTIFY:
148  if (wParam == MCI_NOTIFY_SUCCESSFUL)
150  return true;
151  case WM_KEYUP:
152  if (Game.DoKeyboardInput(scancode, KEYEV_Up, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, false, nullptr))
153  return 0;
154  break;
155  case WM_KEYDOWN:
156  if (Game.DoKeyboardInput(scancode, KEYEV_Down, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(lParam & 0x40000000), nullptr))
157  return 0;
158  break;
159  case WM_SYSKEYDOWN:
160  if (wParam == 18) break;
161  if (Game.DoKeyboardInput(scancode, KEYEV_Down, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(lParam & 0x40000000), nullptr))
162  {
163  // Remove handled message from queue to prevent Windows "standard" sound for unprocessed system message
164  MSG msg;
165  PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE);
166  return 0;
167  }
168  break;
169  case WM_CHAR:
170  {
171  // UTF-8 has 1 to 4 data bytes, and we need a terminating \0
172  char c[5] = {0,0,0,0,0};
173  if(!WideCharToMultiByte(CP_UTF8, 0L, reinterpret_cast<LPCWSTR>(&wParam), 1, c, 4, nullptr, nullptr))
174  return 0;
175  // GUI: forward
176  if (::pGUI->CharIn(c))
177  return 0;
178  return false;
179  }
180  case WM_USER_LOG:
181  if (SEqual2((const char *)lParam, "IDS_"))
182  Log(LoadResStr((const char *)lParam));
183  else
184  Log((const char *)lParam);
185  return false;
186  case WM_LBUTTONDOWN:
187  C4GUI::MouseMove(C4MC_Button_LeftDown,p.x,p.y,wParam, nullptr);
188  break;
189  case WM_LBUTTONUP: C4GUI::MouseMove(C4MC_Button_LeftUp, p.x, p.y, wParam, nullptr); break;
190  case WM_RBUTTONDOWN: C4GUI::MouseMove(C4MC_Button_RightDown, p.x, p.y, wParam, nullptr); break;
191  case WM_RBUTTONUP: C4GUI::MouseMove(C4MC_Button_RightUp, p.x, p.y, wParam, nullptr); break;
192  case WM_LBUTTONDBLCLK: C4GUI::MouseMove(C4MC_Button_LeftDouble, p.x, p.y, wParam, nullptr); break;
193  case WM_RBUTTONDBLCLK: C4GUI::MouseMove(C4MC_Button_RightDouble, p.x, p.y, wParam, nullptr); break;
194  case WM_MOUSEWHEEL:
195  // the coordinates are screen-coordinates here (but only on this uMsg),
196  // we need to convert them to client area coordinates
197  ScreenToClient(hwnd, &p);
198  C4GUI::MouseMove(C4MC_Button_Wheel, p.x, p.y, wParam, nullptr);
199  break;
200  case WM_MOUSEMOVE:
201  C4GUI::MouseMove(C4MC_Button_None, p.x, p.y, wParam, nullptr);
202  // Hide cursor in client area
203  if (NativeCursorShown)
204  {
205  NativeCursorShown = false;
206  ShowCursor(FALSE);
207  }
208  break;
209  case WM_NCMOUSEMOVE:
210  // Show cursor on window frame
211  if (!NativeCursorShown)
212  {
213  NativeCursorShown = true;
214  ShowCursor(TRUE);
215  }
216  break;
217  case WM_SIZE:
218  // Notify app about render window size change
219  switch (wParam)
220  {
221  case SIZE_RESTORED:
222  case SIZE_MAXIMIZED:
224  if(Application.pWindow) // this might be called from C4Window::Init in which case Application.pWindow is not yet set
225  ::SetWindowPos(Application.pWindow->renderwnd, nullptr, 0, 0, p.x, p.y, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOZORDER);
226  break;
227  }
228  break;
229  case WM_INPUTLANGCHANGE:
231  break;
232  case WM_SYSCOMMAND:
233  // The user pressed Alt to open the system menu. This enters a modal
234  // loop which stops us from event processing, so prevent it. Users
235  // can still open the system menu by clicking the window's icon.
236  if ((wParam & 0xFFF0) == SC_KEYMENU && lParam == 0)
237  return 0;
238  break;
239  }
240  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
241 }
242 
243 static C4KeyCode msg2scancode(MSG *msg)
244 {
245  // compute scancode
246  C4KeyCode scancode = (((unsigned int)msg->lParam) >> 16) & 0xFF;
247  bool extended = ((msg->lParam & 0x01000000) != 0);
248  ConvertToUnixScancode(msg->wParam, &scancode, extended);
249  return scancode;
250 }
251 
253 {
254  switch (msg->message)
255  {
256  case WM_KEYDOWN:
257  if (Game.DoKeyboardInput(msg2scancode(msg), KEYEV_Down, !!(msg->lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(msg->lParam & 0x40000000), nullptr)) return true;
258  break;
259  case WM_KEYUP:
260  if (Game.DoKeyboardInput(msg2scancode(msg), KEYEV_Up, !!(msg->lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, false, nullptr)) return false;
261  break;
262  case WM_SYSKEYDOWN:
263  if (msg->wParam == 18) break; // VK_MENU (Alt)
264  if (Game.DoKeyboardInput(msg2scancode(msg), KEYEV_Down, !!(msg->lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(msg->lParam & 0x40000000), nullptr)) return false;
265  break;
266  }
267  return false;
268 
269 }
270 
271 LRESULT APIENTRY ViewportWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
272 {
273  // Determine viewport
274  C4Viewport *cvp;
275  if (!(cvp=::Viewports.GetViewport(hwnd)))
276  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
277 
278  // compute scancode
279  C4KeyCode scancode = (((unsigned int)lParam) >> 16) & 0xFF;
280  bool extended = ((lParam & 0x01000000) != 0);
281  ConvertToUnixScancode(wParam, &scancode, extended);
282 
283  // Process message
284  switch (uMsg)
285  {
286  //---------------------------------------------------------------------------------------------------------------------------
287  case WM_KEYDOWN:
288  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
289  switch (wParam)
290  {
291  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
292  case VK_SCROLL:
293  // key bound to this specific viewport. Don't want to pass this through C4Game...
294  cvp->TogglePlayerLock();
295  break;
296  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
297  default:
298  if (Game.DoKeyboardInput(scancode, KEYEV_Down, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(lParam & 0x40000000), nullptr)) return 0;
299  break;
300  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
301  }
302  break;
303  //---------------------------------------------------------------------------------------------------------------------------
304  case WM_KEYUP:
305  if (Game.DoKeyboardInput(scancode, KEYEV_Up, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, false, nullptr)) return 0;
306  break;
307  //------------------------------------------------------------------------------------------------------------
308  case WM_SYSKEYDOWN:
309  if (wParam == 18) break;
310  if (Game.DoKeyboardInput(scancode, KEYEV_Down, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(lParam & 0x40000000), nullptr)) return 0;
311  break;
312  //----------------------------------------------------------------------------------------------------------------------------------
313  case WM_DESTROY:
314  StoreWindowPosition(hwnd, FormatString("Viewport%i",cvp->Player+1).getData(), Config.GetSubkeyPath("Console"));
315  break;
316  //----------------------------------------------------------------------------------------------------------------------------------
317  case WM_CREATE:
318  DragAcceptFiles(hwnd, TRUE);
319  break;
320  case WM_CLOSE:
321  cvp->pWindow->Close();
322  break;
323 
324  //----------------------------------------------------------------------------------------------------------------------------------
325  case WM_DROPFILES:
326  {
327  HDROP hDrop = (HDROP)(HANDLE) wParam;
328  if (!Console.Editing) { Console.Message(LoadResStr("IDS_CNS_NONETEDIT")); return false; }
329 
330  int32_t iFileNum = DragQueryFile(hDrop,0xFFFFFFFF,nullptr,0);
331  POINT pntPoint;
332  DragQueryPoint(hDrop,&pntPoint);
333  wchar_t szFilename[500+1];
334  for (int32_t cnt=0; cnt<iFileNum; cnt++)
335  {
336  DragQueryFileW(hDrop,cnt,szFilename,500);
337  cvp->DropFile(StdStrBuf(szFilename).getData(), (float)pntPoint.x, (float)pntPoint.y);
338  }
339  DragFinish(hDrop);
340  break;
341  }
342  //----------------------------------------------------------------------------------------------------------------------------------
343  case WM_USER_DROPDEF:
344  Game.DropDef(C4ID(lParam),cvp->GetViewX()+float(LOWORD(wParam))/cvp->Zoom,cvp->GetViewY()+float(HIWORD(wParam)/cvp->Zoom));
345  break;
346  //----------------------------------------------------------------------------------------------------------------------------------
347  case WM_SIZE:
348  case WM_SIZING:
349  cvp->UpdateOutputSize();
350  break;
351  //----------------------------------------------------------------------------------------------------------------------------------
352  case WM_PAINT:
354  break;
355  //----------------------------------------------------------------------------------------------------------------------------------
356  case WM_HSCROLL:
357  switch (LOWORD(wParam))
358  {
359  case SB_THUMBTRACK:
360  case SB_THUMBPOSITION: cvp->SetViewX(float(HIWORD(wParam))/cvp->Zoom); break;
361  case SB_LINELEFT: cvp->ScrollView(-ViewportScrollSpeed, 0.0f); break;
362  case SB_LINERIGHT: cvp->ScrollView(+ViewportScrollSpeed, 0.0f); break;
363  case SB_PAGELEFT: cvp->ScrollView(-cvp->ViewWdt/cvp->Zoom, 0.0f); break;
364  case SB_PAGERIGHT: cvp->ScrollView(+cvp->ViewWdt/cvp->Zoom, 0.0f); break;
365  }
366  cvp->Execute();
368  return 0;
369  //----------------------------------------------------------------------------------------------------------------------------------
370  case WM_VSCROLL:
371  switch (LOWORD(wParam))
372  {
373  case SB_THUMBTRACK:
374  case SB_THUMBPOSITION: cvp->SetViewY(float(HIWORD(wParam))/cvp->Zoom); break;
375  case SB_LINEUP: cvp->ScrollView(0.0f,-ViewportScrollSpeed); break;
376  case SB_LINEDOWN: cvp->ScrollView(0.0f,+ViewportScrollSpeed); break;
377  case SB_PAGEUP: cvp->ScrollView(0.0f,-cvp->ViewWdt/cvp->Zoom); break;
378  case SB_PAGEDOWN: cvp->ScrollView(0.0f,+cvp->ViewWdt/cvp->Zoom); break;
379  }
380  cvp->Execute();
382  return 0;
383  //----------------------------------------------------------------------------------------------------------------------------------
384  case WM_ACTIVATE:
385  // Keep editing dialogs on top of the current viewport, but don't make them
386  // float on other windows (i.e., no HWND_TOPMOST).
387  // Also, don't use SetParent, since that activates the window, which in turn
388  // posts a new WM_ACTIVATE to us, and so on, ultimately leading to a hang.
389  if (LOWORD(wParam) == WA_INACTIVE)
390  {
391  Console.Win32KeepDialogsFloating();
392  }
393  else
394  {
395  // FALLTHROUGH
396  case WM_MOUSEACTIVATE:
397  // WM_MOUSEACTIVATE is emitted when the user hovers over a window and pushes a mouse button.
398  // Setting the window owner here avoids z-order flickering.
399  Console.Win32KeepDialogsFloating(hwnd);
400  }
401  break;
402  //----------------------------------------------------------------------------------------------------------------------------------
403  case WM_SYSCOMMAND:
404  // The user pressed Alt to open the system menu. This enters a modal
405  // loop which stops us from event processing, so prevent it. Users
406  // can still open the system menu by clicking the window's icon.
407  if ((wParam & 0xFFF0) == SC_KEYMENU && lParam == 0)
408  return 0;
409  break;
410  }
411 
412  POINT p;
413  p.x = GET_X_LPARAM(lParam);
414  p.y = GET_Y_LPARAM(lParam);
415 
416  // Viewport mouse control
418  {
419  switch (uMsg)
420  {
421  //----------------------------------------------------------------------------------------------------------------------------------
422  case WM_LBUTTONDOWN: C4GUI::MouseMove(C4MC_Button_LeftDown, p.x, p.y, wParam, cvp); break;
423  //----------------------------------------------------------------------------------------------------------------------------------
424  case WM_LBUTTONUP: C4GUI::MouseMove(C4MC_Button_LeftUp, p.x, p.y, wParam, cvp); break;
425  //----------------------------------------------------------------------------------------------------------------------------------
426  case WM_RBUTTONDOWN: C4GUI::MouseMove(C4MC_Button_RightDown, p.x, p.y, wParam, cvp); break;
427  //----------------------------------------------------------------------------------------------------------------------------------
428  case WM_RBUTTONUP: C4GUI::MouseMove(C4MC_Button_RightUp, p.x, p.y, wParam, cvp); break;
429  //----------------------------------------------------------------------------------------------------------------------------------
430  case WM_LBUTTONDBLCLK: C4GUI::MouseMove(C4MC_Button_LeftDouble, p.x, p.y, wParam, cvp); break;
431  //----------------------------------------------------------------------------------------------------------------------------------
432  case WM_RBUTTONDBLCLK: C4GUI::MouseMove(C4MC_Button_RightDouble, p.x, p.y, wParam, cvp); break;
433  //----------------------------------------------------------------------------------------------------------------------------------
434  case WM_MOUSEMOVE:
435  if ( Inside<int32_t>(p.x-cvp->DrawX,0,cvp->ViewWdt-1)
436  && Inside<int32_t>(p.y-cvp->DrawY,0,cvp->ViewHgt-1) )
437  SetCursor(nullptr);
438  C4GUI::MouseMove(C4MC_Button_None, p.x, p.y, wParam, cvp);
439  break;
440  //----------------------------------------------------------------------------------------------------------------------------------
441  case WM_MOUSEWHEEL:
442  ScreenToClient(hwnd, &p);
443  C4GUI::MouseMove(C4MC_Button_Wheel, p.x, p.y, wParam, cvp);
444  break;
445  //----------------------------------------------------------------------------------------------------------------------------------
446 
447  }
448  }
449  // Console edit cursor control
450  else
451  {
452  // The state of the ALT key is not reported in wParam for mouse messages,
453  // and for keyboard messages the key states are hidden in lParam. It's a mess. Let's just
454  // query GetKeyState().
455  DWORD dwKeyState = 0;
456  if(GetKeyState(VK_CONTROL) < 0) dwKeyState |= MK_CONTROL;
457  if(GetKeyState(VK_SHIFT) < 0) dwKeyState |= MK_SHIFT;
458  if(GetKeyState(VK_MENU) < 0) dwKeyState |= MK_ALT;
459 
460  switch (uMsg)
461  {
462  case WM_KEYDOWN:
463  Console.EditCursor.KeyDown(scancode, dwKeyState);
464  break;
465  case WM_KEYUP:
466  Console.EditCursor.KeyUp(scancode, dwKeyState);
467  break;
468  case WM_SYSKEYDOWN:
469  Console.EditCursor.KeyDown(scancode, dwKeyState);
470  break;
471  case WM_SYSKEYUP:
472  Console.EditCursor.KeyUp(scancode, dwKeyState);
473  break;
474  //----------------------------------------------------------------------------------------------------------------------------------
475  case WM_LBUTTONDOWN:
476  // movement update needed before, so target is always up-to-date
477  cvp->pWindow->EditCursorMove(p.x, p.y, dwKeyState);
478  Console.EditCursor.LeftButtonDown(dwKeyState); break;
479  //----------------------------------------------------------------------------------------------------------------------------------
480  case WM_LBUTTONUP: Console.EditCursor.LeftButtonUp(dwKeyState); break;
481  //----------------------------------------------------------------------------------------------------------------------------------
482  case WM_RBUTTONDOWN: Console.EditCursor.RightButtonDown(dwKeyState); break;
483  //----------------------------------------------------------------------------------------------------------------------------------
484  case WM_RBUTTONUP: Console.EditCursor.RightButtonUp(dwKeyState); break;
485  //----------------------------------------------------------------------------------------------------------------------------------
486  case WM_MOUSEMOVE: cvp->pWindow->EditCursorMove(p.x, p.y, dwKeyState); break;
487  //----------------------------------------------------------------------------------------------------------------------------------
488  }
489  }
490 
491  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
492 }
493 
494 LRESULT APIENTRY DialogWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
495 {
496  // Determine dialog
497  C4GUI::Dialog *pDlg = ::pGUI->GetDialog(hwnd);
498  if (!pDlg) return DefWindowProc(hwnd, uMsg, wParam, lParam);
499 
500  POINT p;
501  p.x = GET_X_LPARAM(lParam);
502  p.y = GET_Y_LPARAM(lParam);
503 
504  // compute scancode
505  C4KeyCode scancode = (((unsigned int)lParam) >> 16) & 0xFF;
506  bool extended = ((lParam & 0x01000000) != 0);
507  ConvertToUnixScancode(wParam, &scancode, extended);
508 
509  // Process message
510  switch (uMsg)
511  {
512  //---------------------------------------------------------------------------------------------------------------------------
513  case WM_KEYDOWN:
514  if (Game.DoKeyboardInput(scancode, KEYEV_Down, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(lParam & 0x40000000), pDlg)) return 0;
515  break;
516  //---------------------------------------------------------------------------------------------------------------------------
517  case WM_KEYUP:
518  if (Game.DoKeyboardInput(scancode, KEYEV_Up, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, false, pDlg)) return 0;
519  break;
520  //------------------------------------------------------------------------------------------------------------
521  case WM_SYSKEYDOWN:
522  if (wParam == 18) break;
523  if (Game.DoKeyboardInput(scancode, KEYEV_Down, !!(lParam & 0x20000000), GetKeyState(VK_CONTROL) < 0, GetKeyState(VK_SHIFT) < 0, !!(lParam & 0x40000000), pDlg)) return 0;
524  break;
525  //----------------------------------------------------------------------------------------------------------------------------------
526  case WM_DESTROY:
527  {
528  const char *szID = pDlg->GetID();
529  if (szID && *szID)
530  StoreWindowPosition(hwnd, FormatString("ConsoleGUI_%s", szID).getData(), Config.GetSubkeyPath("Console"), false);
531  }
532  break;
533  //----------------------------------------------------------------------------------------------------------------------------------
534  case WM_CLOSE:
535  pDlg->Close(false);
536  break;
537  //----------------------------------------------------------------------------------------------------------------------------------
538  case WM_SIZE:
539  // UpdateOutputSize
540  break;
541  //----------------------------------------------------------------------------------------------------------------------------------
542  case WM_PAINT:
543  // 2do: only draw specific dlg?
544  break;
545  return 0;
546  //----------------------------------------------------------------------------------------------------------------------------------
547  case WM_LBUTTONDOWN: ::pGUI->MouseInput(C4MC_Button_LeftDown, p.x, p.y, wParam, pDlg, nullptr); break;
548  //----------------------------------------------------------------------------------------------------------------------------------
549  case WM_LBUTTONUP: ::pGUI->MouseInput(C4MC_Button_LeftUp, p.x, p.y, wParam, pDlg, nullptr); break;
550  //----------------------------------------------------------------------------------------------------------------------------------
551  case WM_RBUTTONDOWN: ::pGUI->MouseInput(C4MC_Button_RightDown, p.x, p.y, wParam, pDlg, nullptr); break;
552  //----------------------------------------------------------------------------------------------------------------------------------
553  case WM_RBUTTONUP: ::pGUI->MouseInput(C4MC_Button_RightUp, p.x, p.y, wParam, pDlg, nullptr); break;
554  //----------------------------------------------------------------------------------------------------------------------------------
555  case WM_LBUTTONDBLCLK: ::pGUI->MouseInput(C4MC_Button_LeftDouble, p.x, p.y, wParam, pDlg, nullptr); break;
556  //----------------------------------------------------------------------------------------------------------------------------------
557  case WM_RBUTTONDBLCLK: ::pGUI->MouseInput(C4MC_Button_RightDouble, p.x, p.y, wParam, pDlg, nullptr); break;
558  //----------------------------------------------------------------------------------------------------------------------------------
559  case WM_MOUSEMOVE:
560  ::pGUI->MouseInput(C4MC_Button_None, p.x, p.y, wParam, pDlg, nullptr);
561  break;
562  //----------------------------------------------------------------------------------------------------------------------------------
563  case WM_MOUSEWHEEL:
564  ScreenToClient(hwnd, &p);
565  ::pGUI->MouseInput(C4MC_Button_Wheel, p.x, p.y, wParam, pDlg, nullptr);
566  break;
567  //----------------------------------------------------------------------------------------------------------------------------------
568  }
569 
570  return DefWindowProc(hwnd, uMsg, wParam, lParam);
571 }
572 
573 C4Window::C4Window () = default;
574 C4Window::~C4Window () = default;
575 
576 C4Window * C4Window::Init(C4Window::WindowKind windowKind, C4AbstractApp * pApp, const char * Title, const C4Rect * size)
577 {
578  Active = true;
579  eKind = windowKind;
580  if (windowKind == W_Viewport)
581  {
582 #ifdef WITH_QT_EDITOR
583  // embed into editor: Viewport widget creation handled by C4ConsoleQt
584  ::Console.AddViewport(static_cast<C4ViewportWindow *>(this));
585  return this;
586 #else
587  static bool fViewportClassRegistered = false;
588  if (!fViewportClassRegistered)
589  {
590  // Register viewport class
591  WNDCLASSEXW WndClass;
592  WndClass.cbSize=sizeof(WNDCLASSEX);
593  WndClass.style = CS_DBLCLKS | CS_BYTEALIGNCLIENT;
594  WndClass.lpfnWndProc = ViewportWinProc;
595  WndClass.cbClsExtra = 0;
596  WndClass.cbWndExtra = 0;
597  WndClass.hInstance = pApp->GetInstance();
598  WndClass.hCursor = LoadCursor (nullptr, IDC_ARROW);
599  WndClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
600  WndClass.lpszMenuName = nullptr;
601  WndClass.lpszClassName = C4ViewportClassName;
602  WndClass.hIcon = LoadIcon (pApp->GetInstance(), MAKEINTRESOURCE (IDI_01_OCS) );
603  WndClass.hIconSm = LoadIcon (pApp->GetInstance(), MAKEINTRESOURCE (IDI_01_OCS) );
604  if (!RegisterClassExW(&WndClass)) return nullptr;
605  fViewportClassRegistered = true;
606  }
607  // Create window
608  hWindow = CreateWindowExW (
609  WS_EX_ACCEPTFILES,
611  CW_USEDEFAULT,CW_USEDEFAULT, size->Wdt, size->Hgt,
612  Console.hWindow,nullptr,pApp->GetInstance(),nullptr);
613  if(!hWindow) return nullptr;
614 #endif
615  // We don't re-init viewport windows currently, so we don't need a child window
616  // for now: Render into main window.
617  renderwnd = hWindow;
618  }
619  else if (windowKind == W_Fullscreen)
620  {
621  // Register window class
622  auto WndClass = WNDCLASSEXW();
623  WndClass.cbSize = sizeof(WNDCLASSEX);
624  WndClass.style = CS_DBLCLKS;
625  WndClass.lpfnWndProc = FullScreenWinProc;
626  WndClass.hInstance = pApp->GetInstance();
627  WndClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
628  WndClass.lpszClassName = C4FullScreenClassName;
629  WndClass.hIcon = LoadIcon (pApp->GetInstance(), MAKEINTRESOURCE (IDI_00_C4X) );
630  WndClass.hIconSm = LoadIcon (pApp->GetInstance(), MAKEINTRESOURCE (IDI_00_C4X) );
631  if (!RegisterClassExW(&WndClass)) return nullptr;
632 
633  // Create window
634  hWindow = CreateWindowExW (
635  0,
637  GetWideChar(Title),
638  WS_OVERLAPPEDWINDOW,
639  CW_USEDEFAULT,CW_USEDEFAULT, size->Wdt, size->Hgt,
640  nullptr,nullptr,pApp->GetInstance(),nullptr);
641  if(!hWindow) return nullptr;
642 
643  RECT rc;
644  GetClientRect(hWindow, &rc);
645  renderwnd = CreateWindowExW(0, L"STATIC", nullptr, WS_CHILD,
646  0, 0, rc.right - rc.left, rc.bottom - rc.top,
647  hWindow, nullptr, pApp->GetInstance(), nullptr);
648  if(!renderwnd) { DestroyWindow(hWindow); return nullptr; }
649  ShowWindow(renderwnd, SW_SHOW);
650 
651  #ifndef USE_CONSOLE
652  // Show & focus
653  ShowWindow(hWindow,SW_SHOWNORMAL);
654  SetFocus(hWindow);
655  #endif
656  }
657  else if (windowKind == W_GuiWindow)
658  {
659  static bool fDialogClassRegistered = false;
660  if (!fDialogClassRegistered)
661  {
662  // register landscape viewport class
663  WNDCLASSEXW WndClass;
664  WndClass.cbSize=sizeof(WNDCLASSEX);
665  WndClass.style = CS_DBLCLKS | CS_BYTEALIGNCLIENT;
666  WndClass.lpfnWndProc = DialogWinProc;
667  WndClass.cbClsExtra = 0;
668  WndClass.cbWndExtra = 0;
669  WndClass.hInstance = pApp->GetInstance();
670  WndClass.hCursor = LoadCursor (nullptr, IDC_ARROW); // - always use normal hw cursor
671  WndClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
672  WndClass.lpszMenuName = nullptr;
673  WndClass.lpszClassName = ConsoleDlgClassName;
674  WndClass.hIcon = LoadIcon (pApp->GetInstance(), MAKEINTRESOURCE (IDI_00_C4X) );
675  WndClass.hIconSm = LoadIcon (pApp->GetInstance(), MAKEINTRESOURCE (IDI_00_C4X) );
676  if (!RegisterClassExW(&WndClass))
677  return nullptr;
678  }
679  Active = true;
680  // calculate required size
681  RECT rtSize;
682  rtSize.left = 0;
683  rtSize.top = 0;
684  rtSize.right = size->Wdt;
685  rtSize.bottom = size->Hgt;
686  if (!::AdjustWindowRectEx(&rtSize, ConsoleDlgWindowStyle, false, 0))
687  return nullptr;
688  // create it!
689  if (!Title || !*Title) Title = "???";
690  hWindow = ::CreateWindowExW(
691  0,
694  CW_USEDEFAULT,CW_USEDEFAULT,rtSize.right-rtSize.left,rtSize.bottom-rtSize.top,
695  ::Console.hWindow,nullptr,pApp->GetInstance(),nullptr);
696  renderwnd = hWindow;
697  return hWindow ? this : nullptr;
698  }
699  else if (windowKind == W_Control)
700  {
701  // controlled externally
702  hWindow = renderwnd = nullptr;
703  }
704  return this;
705 }
706 
708 {
709  // We don't need to change anything with the window for any
710  // configuration option changes on Windows.
711 
712  // However, re-create the render window so that another pixel format can
713  // be chosen for it. The pixel format is chosen in CStdGLCtx::Init.
714 
715  RECT rc;
716  GetClientRect(hWindow, &rc);
717  HWND hNewRenderWindow = CreateWindowExW(0, L"STATIC", nullptr, WS_CHILD,
718  0, 0, rc.right - rc.left, rc.bottom - rc.top,
719  hWindow, nullptr, pApp->hInstance, nullptr);
720  if(!hNewRenderWindow) return false;
721 
722  ShowWindow(hNewRenderWindow, SW_SHOW);
723  DestroyWindow(renderwnd);
724  renderwnd = hNewRenderWindow;
725 
726  return true;
727 }
728 
729 void C4Window::Clear()
730 {
731  // Destroy window if we own it
732  if (eKind != W_Control)
733  {
734  if (renderwnd) DestroyWindow(renderwnd);
735  if (hWindow && hWindow != renderwnd) DestroyWindow(hWindow);
736  }
737 #ifdef WITH_QT_EDITOR
738  if (eKind == W_Viewport)
739  {
740  // embed into editor: Viewport widget creation handled by C4ConsoleQt
741  ::Console.RemoveViewport(static_cast<C4ViewportWindow *>(this));
742  }
743 #endif
744  renderwnd = nullptr;
745  hWindow = nullptr;
746 }
747 
748 bool C4Window::StorePosition(const char *szWindowName, const char *szSubKey, bool fStoreSize)
749 {
750  return StoreWindowPosition(hWindow, szWindowName, szSubKey, fStoreSize) != 0;
751 }
752 
753 bool C4Window::RestorePosition(const char *szWindowName, const char *szSubKey, bool fHidden)
754 {
755  if (!RestoreWindowPosition(hWindow, szWindowName, szSubKey, fHidden))
756  ShowWindow(hWindow,SW_SHOWNORMAL);
757  return true;
758 }
759 
760 void C4Window::SetTitle(const char *szToTitle)
761 {
762  if (hWindow) SetWindowTextW(hWindow, GetWideChar(szToTitle ? szToTitle : ""));
763 }
764 
765 bool C4Window::GetSize(C4Rect * pRect)
766 {
767  RECT r;
768  if (!(hWindow && GetClientRect(hWindow,&r))) return false;
769  pRect->x = r.left;
770  pRect->y = r.top;
771  pRect->Wdt = r.right - r.left;
772  pRect->Hgt = r.bottom - r.top;
773  return true;
774 }
775 
776 void C4Window::SetSize(unsigned int cx, unsigned int cy)
777 {
778  if (hWindow)
779  {
780  // If bordered, add border size
781  RECT rect = { 0, 0, static_cast<LONG>(cx), static_cast<LONG>(cy) };
782  ::AdjustWindowRectEx(&rect, GetWindowLong(hWindow, GWL_STYLE), FALSE, GetWindowLong(hWindow, GWL_EXSTYLE));
783  cx = rect.right - rect.left;
784  cy = rect.bottom - rect.top;
785  ::SetWindowPos(hWindow, nullptr, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOZORDER);
786 
787  // Also resize child window
788  GetClientRect(hWindow, &rect);
789  ::SetWindowPos(renderwnd, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOZORDER);
790  }
791 }
792 
794 {
795  // please activate me!
796  if (hWindow)
797  ::FlashWindow(hWindow, FLASHW_ALL | FLASHW_TIMERNOFG);
798 }
799 
800 void C4Window::GrabMouse(bool grab)
801 {
802  // TODO
803 }
804 
805 void C4Window::EnumerateMultiSamples(std::vector<int>& samples, int) const
806 {
807 #ifndef USE_CONSOLE
808  if(pGL && pGL->pMainCtx)
809  samples = pGL->pMainCtx->EnumerateMultiSamples();
810 #endif
811 }
812 
813 /* CStdMessageProc */
814 
815 bool CStdMessageProc::Execute(int iTimeout, pollfd *)
816 {
817  // Peek messages
818  MSG msg;
819  while (PeekMessage(&msg,nullptr,0,0,PM_REMOVE))
820  {
821  // quit?
822  if (msg.message == WM_QUIT)
823  {
824  pApp->fQuitMsgReceived = true;
825  return false;
826  }
827  // Dialog message transfer
828  if (!pApp->pWindow || !pApp->pWindow->Win32DialogMessageHandling(&msg))
829  {
830  TranslateMessage(&msg); DispatchMessage(&msg);
831  }
832  }
833  return true;
834 }
835 
836 /* C4AbstractApp */
837 
839 {
840  ZeroMemory(&dspMode, sizeof(dspMode)); dspMode.dmSize = sizeof(dspMode);
841  ZeroMemory(&OldDspMode, sizeof(OldDspMode)); OldDspMode.dmSize = sizeof(OldDspMode);
842  idMainThread = 0;
843 #ifdef _WIN32
844  MessageProc.SetApp(this);
845  Add(&MessageProc);
846 #endif
847 }
848 
850 
851 bool C4AbstractApp::Init(int argc, char * argv[])
852 {
853  // Set instance vars
854  idMainThread = ::GetCurrentThreadId();
855  // Custom initialization
856  return DoInit (argc, argv);
857 }
858 
860 {
861  idMainThread = 0;
862 }
863 
864 void C4AbstractApp::Quit()
865 {
866  PostQuitMessage(0);
867 }
868 
870 {
871 
872  // Always fail after quit message
873  if (fQuitMsgReceived)
874  return false;
875 
876  return MessageProc.Execute(0);
877 }
878 
879 void C4AbstractApp::SetLastErrorFromOS()
880 {
881  LPWSTR buffer = nullptr;
882  FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
883  nullptr, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&buffer), 0, nullptr);
884  sLastError = WStrToString(buffer);
885  LocalFree(buffer);
886 }
887 
889 
890 static BOOL CALLBACK GLMonitorInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
891 {
892  // get to indexed monitor
893  if (GLMonitorInfoEnumCount--) return true;
894  // store it
895  C4AbstractApp *pApp = (C4AbstractApp *) dwData;
896  pApp->hMon = hMonitor;
897  pApp->MonitorRect = *lprcMonitor;
898  return true;
899 }
900 
901 bool C4AbstractApp::GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32_t *piYRes, int32_t *piBitDepth, int32_t *piRefreshRate, uint32_t iMonitor)
902 {
903  // prepare search struct
904  DEVMODEW dmode;
905  ZeroMemory(&dmode, sizeof(dmode)); dmode.dmSize = sizeof(dmode);
906  StdStrBuf Mon;
907  if (iMonitor)
908  Mon.Format(R"(\\.\Display%d)", iMonitor+1);
909  // check if indexed mode exists
910  if (!EnumDisplaySettingsW(Mon.GetWideChar(), iIndex, &dmode))
911  {
912  SetLastErrorFromOS();
913  return false;
914  }
915  // mode exists; return it
916  if (piXRes) *piXRes = dmode.dmPelsWidth;
917  if (piYRes) *piYRes = dmode.dmPelsHeight;
918  if (piBitDepth) *piBitDepth = dmode.dmBitsPerPel;
919  if (piRefreshRate) *piRefreshRate = dmode.dmDisplayFrequency;
920  return true;
921 }
922 
924 {
925 }
926 
927 bool C4AbstractApp::SetVideoMode(int iXRes, int iYRes, unsigned int iRefreshRate, unsigned int iMonitor, bool fFullScreen)
928 {
929 #ifndef USE_CONSOLE
930  SetWindowLong(pWindow->hWindow, GWL_EXSTYLE,
931  GetWindowLong(pWindow->hWindow, GWL_EXSTYLE) | WS_EX_APPWINDOW);
932  // change mode
933  if (!fFullScreen)
934  {
935 
936  ChangeDisplaySettings(nullptr, 0);
937  SetWindowLong(pWindow->hWindow, GWL_STYLE,
938  GetWindowLong(pWindow->hWindow, GWL_STYLE) | (WS_CAPTION|WS_THICKFRAME|WS_BORDER));
939  if(iXRes != -1 && iYRes != -1) {
940  pWindow->SetSize(iXRes, iYRes);
941  OnResolutionChanged(iXRes, iYRes);
942  }
943  ::SetWindowPos(pWindow->hWindow, nullptr, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW|SWP_FRAMECHANGED);
944  }
945  else
946  {
947  bool fFound=false;
948  DEVMODEW dmode;
949  // if a monitor is given, search on that instead
950  // get monitor infos
951  GLMonitorInfoEnumCount = iMonitor;
952  hMon = nullptr;
953  EnumDisplayMonitors(nullptr, nullptr, GLMonitorInfoEnumProc, (LPARAM) this);
954  // no monitor assigned?
955  if (!hMon)
956  {
957  // Okay for primary; then just use a default
958  if (!iMonitor)
959  {
960  MonitorRect.left = MonitorRect.top = 0;
961  MonitorRect.right = iXRes;
962  MonitorRect.bottom = iYRes;
963  }
964  else return false;
965  }
966  StdStrBuf Mon;
967  if (iMonitor)
968  Mon.Format(R"(\\.\Display%d)", iMonitor+1);
969 
970  ZeroMemory(&dmode, sizeof(dmode));
971  dmode.dmSize = sizeof(dmode);
972 
973  // Get current display settings
974  if (!EnumDisplaySettingsW(Mon.GetWideChar(), ENUM_CURRENT_SETTINGS, &dmode))
975  {
976  SetLastErrorFromOS();
977  return false;
978  }
979  unsigned long orientation = dmode.dmDisplayOrientation;
980  if (iXRes == -1 && iYRes == -1)
981  {
982  dspMode=dmode;
983  fFound = true;
984  }
985  // enumerate modes
986  int i=0;
987  if (!fFound) while (EnumDisplaySettingsW(Mon.GetWideChar(), i++, &dmode))
988  // compare enumerated mode with requested settings
989  if (static_cast<int>(dmode.dmPelsWidth) == iXRes && static_cast<int>(dmode.dmPelsHeight) == iYRes && dmode.dmBitsPerPel == C4Draw::COLOR_DEPTH && dmode.dmDisplayOrientation == orientation
990  && (iRefreshRate == 0 || dmode.dmDisplayFrequency == iRefreshRate))
991  {
992  fFound=true;
993  dspMode=dmode;
994  break;
995  }
996  if (!fFound) return false;
997 
998  dspMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
999  if (iRefreshRate != 0)
1000  dspMode.dmFields |= DM_DISPLAYFREQUENCY;
1001  LONG rv = ChangeDisplaySettingsExW(iMonitor ? Mon.GetWideChar() : nullptr, &dspMode, nullptr, CDS_FULLSCREEN, nullptr);
1002  if (rv != DISP_CHANGE_SUCCESSFUL)
1003  {
1004  switch (rv)
1005  {
1006 #define CDSE_ERROR(error) case error: sLastError = LoadResStr("IDS_ERR_" #error); break
1007  CDSE_ERROR(DISP_CHANGE_BADFLAGS);
1008  CDSE_ERROR(DISP_CHANGE_BADMODE);
1009  CDSE_ERROR(DISP_CHANGE_BADPARAM);
1010  CDSE_ERROR(DISP_CHANGE_RESTART);
1011  CDSE_ERROR(DISP_CHANGE_FAILED);
1012 #undef CDSE_ERROR
1013  default:
1014  sLastError = LoadResStr("IDS_ERR_FAILURE");
1015  break;
1016  }
1017  return false;
1018  }
1019 
1020  SetWindowLong(pWindow->hWindow, GWL_STYLE,
1021  GetWindowLong(pWindow->hWindow, GWL_STYLE) & ~ (WS_CAPTION|WS_THICKFRAME|WS_BORDER));
1022 
1023  pWindow->SetSize(dspMode.dmPelsWidth, dspMode.dmPelsHeight);
1024  OnResolutionChanged(dspMode.dmPelsWidth, dspMode.dmPelsHeight);
1025  ::SetWindowPos(pWindow->hWindow, nullptr, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW|SWP_FRAMECHANGED);
1026  }
1027  return true;
1028 #endif
1029 }
1030 
1031 void C4AbstractApp::MessageDialog(const char * message)
1032 {
1033  MessageBoxW(nullptr, GetWideChar(message), ADDL(C4ENGINECAPTION), MB_ICONERROR);
1034 }
1035 
1036 // Clipboard functions
1037 bool C4AbstractApp::Copy(const std::string &text, bool fClipboard)
1038 {
1039  if (!fClipboard) return false;
1040  bool fSuccess = true;
1041  // gain clipboard ownership
1042  if (!OpenClipboard(pWindow ? pWindow->hWindow : nullptr)) return false;
1043  // must empty the global clipboard, so the application clipboard equals the Windows clipboard
1044  EmptyClipboard();
1045  int size = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), text.size() + 1, nullptr, 0);
1046  HANDLE hglbCopy = GlobalAlloc(GMEM_MOVEABLE, size * sizeof(wchar_t));
1047  if (hglbCopy == nullptr) { CloseClipboard(); return false; }
1048  // lock the handle and copy the text to the buffer.
1049  wchar_t *szCopyChar = (wchar_t *) GlobalLock(hglbCopy);
1050  fSuccess = !!MultiByteToWideChar(CP_UTF8, 0, text.c_str(), text.size() + 1, szCopyChar, size);
1051  GlobalUnlock(hglbCopy);
1052  // place the handle on the clipboard.
1053  fSuccess = fSuccess && !!SetClipboardData(CF_UNICODETEXT, hglbCopy);
1054  // close clipboard
1055  CloseClipboard();
1056  // return whether copying was successful
1057  return fSuccess;
1058 }
1059 
1060 std::string C4AbstractApp::Paste(bool fClipboard)
1061 {
1062  if (!fClipboard) return std::string();
1063  // open clipboard
1064  if (!OpenClipboard(nullptr)) return std::string();
1065  // get text from clipboard
1066  HANDLE hglb = GetClipboardData(CF_UNICODETEXT);
1067  if (!hglb) return std::string();
1068  std::string text{ WStrToString((wchar_t*)GlobalLock(hglb)) };
1069  // unlock mem
1070  GlobalUnlock(hglb);
1071  // close clipboard
1072  CloseClipboard();
1073  return text;
1074 }
1075 
1076 bool C4AbstractApp::IsClipboardFull(bool fClipboard)
1077 {
1078  if (!fClipboard) return false;
1079  return !!IsClipboardFormatAvailable(CF_UNICODETEXT);
1080 }
1081 
1083 {
1084  // just invoke directly
1085  PerformUpdate();
1086 }
1087 
#define WM_USER_DROPDEF
#define WM_USER_LOG
#define s
C4Config Config
Definition: C4Config.cpp:930
const int C4CNS_ModePlay
Definition: C4Console.h:30
CStdGL * pGL
Definition: C4DrawGL.cpp:907
C4Game Game
Definition: C4Globals.cpp:52
C4Console Console
Definition: C4Globals.cpp:45
C4Application Application
Definition: C4Globals.cpp:44
C4MouseControl MouseControl
Definition: C4Globals.cpp:47
C4FullScreen FullScreen
Definition: C4Globals.cpp:46
C4GraphicsSystem GraphicsSystem
Definition: C4Globals.cpp:51
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
@ KEYEV_Up
@ KEYEV_Down
unsigned long C4KeyCode
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
const int32_t C4MC_Button_None
const int32_t C4MC_Button_RightDown
const int32_t C4MC_Button_RightUp
const int32_t C4MC_Button_RightDouble
const int32_t C4MC_Button_LeftUp
const int32_t C4MC_Button_LeftDown
const int32_t C4MC_Button_LeftDouble
const int32_t C4MC_Button_Wheel
C4ViewportList Viewports
#define C4ViewportWindowStyle
@ ViewportScrollSpeed
LRESULT APIENTRY ViewportWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
#define ConsoleDlgWindowStyle
#define CDSE_ERROR(error)
bool ConsoleHandleWin32KeyboardMessage(MSG *msg)
#define C4ViewportClassName
LRESULT APIENTRY FullScreenWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
#define ConsoleDlgClassName
LRESULT APIENTRY DialogWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
#define C4FullScreenClassName
int GLMonitorInfoEnumCount
#define GET_Y_LPARAM(lp)
#define ADDL(s)
StdStrBuf::wchar_t_holder GetWideChar(const char *utf8, bool double_null_terminate=false)
#define GET_X_LPARAM(lp)
uint32_t DWORD
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:204
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
void MessageDialog(const char *message)
Definition: C4AppMac.mm:63
std::string sLastError
Definition: C4App.h:156
bool SetVideoMode(int iXRes, int iYRes, unsigned int iRefreshRate, unsigned int iMonitor, bool fFullScreen)
Definition: C4AppSDL.cpp:354
bool IsClipboardFull(bool fClipboard=true)
Definition: C4AppMac.mm:58
bool fQuitMsgReceived
Definition: C4App.h:81
virtual void Quit()
Definition: C4AppSDL.cpp:110
virtual void OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)=0
bool GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32_t *piYRes, int32_t *piBitDepth, int32_t *piRefreshRate, uint32_t iMonitor)
Definition: C4AppSDL.cpp:335
~C4AbstractApp() override
Definition: C4AppT.cpp:40
bool Active
Definition: C4App.h:63
bool FlushMessages()
Definition: C4AppSDL.cpp:115
void RestoreVideoMode()
Definition: C4AppSDL.cpp:443
bool Copy(const std::string &text, bool fClipboard=true)
Definition: C4AppMac.mm:34
std::string Paste(bool fClipboard=true)
Definition: C4AppMac.mm:47
virtual bool DoInit(int argc, char *argv[])=0
C4Window * pWindow
Definition: C4App.h:80
bool Init(int argc, char *argv[])
Definition: C4AppSDL.cpp:87
virtual void Clear()
Definition: C4AppSDL.cpp:105
const char * GetLastError()
Definition: C4App.h:98
void OnKeyboardLayoutChanged() override
C4MusicSystem MusicSystem
Definition: C4Application.h:41
int GetConfigWidth()
Definition: C4Application.h:83
int GetConfigHeight()
Definition: C4Application.h:84
void OnResolutionChanged(unsigned int iXRes, unsigned int iYRes) override
void Quit() override
int32_t RefreshRate
Definition: C4Config.h:106
int32_t Monitor
Definition: C4Config.h:112
const char * GetSubkeyPath(const char *subkey)
Definition: C4Config.cpp:806
C4ConfigGraphics Graphics
Definition: C4Config.h:257
void AddViewport(C4ViewportWindow *cvp)
Definition: C4ConsoleGUI.h:110
void RemoveViewport(C4ViewportWindow *cvp)
Definition: C4ConsoleGUI.h:111
bool Message(const char *szMessage, bool fQuery=false)
Definition: C4Console.cpp:292
C4EditCursor EditCursor
Definition: C4Console.h:90
static constexpr int COLOR_DEPTH
Definition: C4Draw.h:89
bool LeftButtonDown(DWORD dwKeyState)
int32_t GetMode()
bool KeyDown(C4KeyCode KeyCode, DWORD dwKeyState)
bool RightButtonDown(DWORD dwKeyState)
bool RightButtonUp(DWORD dwKeyState)
bool LeftButtonUp(DWORD dwKeyState)
bool KeyUp(C4KeyCode KeyCode, DWORD dwKeyState)
void Close() override
void Close(bool fOK)
virtual const char * GetID()
Definition: C4Gui.h:2108
virtual bool CharIn(const char *c)
Definition: C4Gui.cpp:779
bool MouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, Dialog *pDlg, class C4Viewport *pVP)
Definition: C4Gui.cpp:837
Dialog * GetDialog(C4Window *pWindow)
Definition: C4Gui.cpp:720
bool DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType event_type, bool alt, bool ctrl, bool shift, bool repeated, class C4GUI::Dialog *for_dialog=nullptr, bool fPlrCtrlOnly=false, int32_t strength=-1)
Definition: C4Game.cpp:2288
bool DropDef(C4ID id, float x, float y)
Definition: C4Game.cpp:1746
Definition: C4Id.h:26
bool IsViewport(C4Viewport *pViewport)
Definition: C4Rect.h:28
int32_t y
Definition: C4Rect.h:30
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
int32_t x
Definition: C4Rect.h:30
float Zoom
Definition: C4Viewport.h:104
int32_t ViewWdt
Definition: C4Viewport.h:36
bool UpdateOutputSize(int32_t new_width=0, int32_t new_height=0)
Definition: C4Viewport.cpp:52
std::unique_ptr< C4ViewportWindow > pWindow
Definition: C4Viewport.h:113
float GetViewY()
Definition: C4Viewport.h:78
void SetViewX(float x)
Definition: C4Viewport.cpp:888
int32_t ViewHgt
Definition: C4Viewport.h:37
bool ScrollBarsByViewPosition()
Definition: C4Console.cpp:710
void ScrollView(float by_x, float by_y)
Definition: C4Viewport.cpp:882
void SetViewY(float y)
Definition: C4Viewport.cpp:907
float GetViewX()
Definition: C4Viewport.h:76
bool TogglePlayerLock()
Definition: C4Console.cpp:711
void DropFile(const char *filename, float x, float y)
Definition: C4Viewport.cpp:47
int32_t DrawY
Definition: C4Viewport.h:41
void Execute()
Definition: C4Viewport.cpp:443
int32_t Player
Definition: C4Viewport.h:108
int32_t DrawX
Definition: C4Viewport.h:40
C4Viewport * GetViewport(int32_t player_nr, C4Viewport *prev=nullptr)
virtual void EnumerateMultiSamples(std::vector< int > &samples, int min_expected=0) const
Definition: C4AppT.cpp:105
virtual C4Window * Init(WindowKind windowKind, C4AbstractApp *pApp, const char *Title, const C4Rect *size)
Definition: C4AppT.cpp:109
bool GetSize(C4Rect *pRect)
Definition: C4AppT.cpp:108
virtual bool ReInit(C4AbstractApp *pApp)
Definition: C4AppT.cpp:110
virtual ~C4Window()
Definition: C4WindowSDL.cpp:46
void SetTitle(const char *Title)
Definition: C4AppT.cpp:114
virtual void PerformUpdate()
Definition: C4App.cpp:85
bool RestorePosition(const char *szWindowName, const char *szSubKey, bool fHidden=false)
Definition: C4AppT.cpp:111
virtual void Clear()
Definition: C4AppT.cpp:102
void FlashWindow()
Definition: C4AppMac.mm:75
void SetSize(unsigned int cx, unsigned int cy)
Definition: C4AppT.cpp:113
WindowKind eKind
Definition: C4Window.h:276
virtual void RequestUpdate()
Definition: C4AppT.cpp:112
bool StorePosition(const char *szWindowName, const char *szSubKey, bool fStoreSize=true)
bool Active
Definition: C4Window.h:274
@ W_Fullscreen
Definition: C4Window.h:268
@ W_GuiWindow
Definition: C4Window.h:265
@ W_Control
Definition: C4Window.h:269
@ W_Viewport
Definition: C4Window.h:267
void GrabMouse(bool grab)
Definition: C4AppT.cpp:107
CStdGLCtx * pMainCtx
Definition: C4DrawGL.h:179
void Add(StdSchedulerProc *pProc)
const char * getData() const
Definition: StdBuf.h:442
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP)
Definition: C4Gui.h:2832
#define IDI_00_C4X
Definition: resource.h:69
#define IDI_01_OCS
Definition: resource.h:70