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