OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4AppSDL.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 /* A wrapper class to OS dependent event and window interfaces, SDL version */
18 
19 #include "C4Include.h"
20 #include "platform/C4App.h"
21 
22 #include "platform/C4Window.h"
23 #include "graphics/C4DrawGL.h"
24 #include "platform/StdFile.h"
25 #include "lib/StdBuf.h"
26 #include "gui/C4MouseControl.h"
27 #include "game/C4Application.h"
28 #include "gui/C4Gui.h"
29 #include "platform/C4GamePadCon.h"
30 #include "C4Version.h"
31 
32 static void sdlToC4MCBtn(const SDL_MouseButtonEvent &e, int32_t& button, DWORD& flags)
33 {
34  button = C4MC_Button_None;
35  flags = SDL_GetModState();
36 
37  switch (e.button)
38  {
39  case SDL_BUTTON_LEFT:
40  if (e.state == SDL_PRESSED)
41  button = e.clicks == 2 ? C4MC_Button_LeftDouble : C4MC_Button_LeftDown;
42  else
43  button = C4MC_Button_LeftUp;
44  break;
45  case SDL_BUTTON_RIGHT:
46  if (e.state == SDL_PRESSED)
47  button = e.clicks == 2 ? C4MC_Button_RightDouble : C4MC_Button_RightDown;
48  else
49  button = C4MC_Button_RightUp;
50  break;
51  case SDL_BUTTON_MIDDLE:
52  if (e.state == SDL_PRESSED)
53  button = e.clicks == 2 ? C4MC_Button_MiddleDouble : C4MC_Button_MiddleDown;
54  else
55  button = C4MC_Button_MiddleUp;
56  break;
57  case SDL_BUTTON_X1:
58  if (e.state == SDL_PRESSED)
59  button = e.clicks == 2 ? C4MC_Button_X1Double : C4MC_Button_X1Down;
60  else
61  button = C4MC_Button_X1Up;
62  break;
63  case SDL_BUTTON_X2:
64  if (e.state == SDL_PRESSED)
65  button = e.clicks == 2 ? C4MC_Button_X2Double : C4MC_Button_X2Down;
66  else
67  button = C4MC_Button_X2Up;
68  break;
69  }
70 }
71 
72 /* C4AbstractApp */
73 
75  Active(false), pWindow(nullptr), fQuitMsgReceived(false),
76  // main thread
77 #ifdef HAVE_PTHREAD
78  MainThread (pthread_self()),
79 #endif
80 #ifdef _WIN32
81  idMainThread(0),
82 #endif
83  fDspModeSet(false)
84 {
85 }
86 
88 {
89 }
90 
91 bool C4AbstractApp::Init(int argc, char * argv[])
92 {
93  // Set locale
94  setlocale(LC_ALL,"");
95 
96  if (SDL_Init(SDL_INIT_VIDEO) != 0)
97  {
98  LogF("SDL_Init: %s", SDL_GetError());
99  return false;
100  }
101 
102  //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
103 
104  // Custom initialization
105  return DoInit (argc, argv);
106 }
107 
108 
110 {
111  SDL_Quit();
112 }
113 
115 {
116  fQuitMsgReceived = true;
117 }
118 
120 {
121  // Always fail after quit message
122  if (fQuitMsgReceived)
123  return false;
124 
125  // Handle pending SDL messages
126  SDL_Event event;
127  while (SDL_PollEvent(&event))
128  {
129  HandleSDLEvent(event);
130  }
131  return true;
132 }
133 
134 #define SDL_SCANCODE_KEYCODE \
135  X(SDL_SCANCODE_LSHIFT, K_SHIFT_L) \
136  X(SDL_SCANCODE_RSHIFT, K_SHIFT_R) \
137  X(SDL_SCANCODE_LCTRL, K_CONTROL_L) \
138  X(SDL_SCANCODE_RCTRL, K_CONTROL_R) \
139  X(SDL_SCANCODE_LALT, K_ALT_L) \
140  X(SDL_SCANCODE_RALT, K_ALT_R) \
141  X(SDL_SCANCODE_F1, K_F1) \
142  X(SDL_SCANCODE_F2, K_F2) \
143  X(SDL_SCANCODE_F3, K_F3) \
144  X(SDL_SCANCODE_F4, K_F4) \
145  X(SDL_SCANCODE_F5, K_F5) \
146  X(SDL_SCANCODE_F6, K_F6) \
147  X(SDL_SCANCODE_F7, K_F7) \
148  X(SDL_SCANCODE_F8, K_F8) \
149  X(SDL_SCANCODE_F9, K_F9) \
150  X(SDL_SCANCODE_F10, K_F10) \
151  X(SDL_SCANCODE_F11, K_F11) \
152  X(SDL_SCANCODE_F12, K_F12) \
153  X(SDL_SCANCODE_KP_PLUS, K_ADD) \
154  X(SDL_SCANCODE_KP_MINUS, K_SUBTRACT) \
155  X(SDL_SCANCODE_KP_MULTIPLY, K_MULTIPLY) \
156  X(SDL_SCANCODE_ESCAPE, K_ESCAPE) \
157  X(SDL_SCANCODE_PAUSE, K_PAUSE) \
158  X(SDL_SCANCODE_TAB, K_TAB) \
159  X(SDL_SCANCODE_RETURN, K_RETURN) \
160  X(SDL_SCANCODE_DELETE, K_DELETE) \
161  X(SDL_SCANCODE_INSERT, K_INSERT) \
162  X(SDL_SCANCODE_BACKSPACE, K_BACK) \
163  X(SDL_SCANCODE_SPACE, K_SPACE) \
164  X(SDL_SCANCODE_UP, K_UP) \
165  X(SDL_SCANCODE_DOWN, K_DOWN) \
166  X(SDL_SCANCODE_LEFT, K_LEFT) \
167  X(SDL_SCANCODE_RIGHT, K_RIGHT) \
168  X(SDL_SCANCODE_HOME, K_HOME) \
169  X(SDL_SCANCODE_END, K_END) \
170  X(SDL_SCANCODE_SCROLLLOCK, K_SCROLL) \
171  X(SDL_SCANCODE_MENU, K_MENU) \
172  X(SDL_SCANCODE_PAGEUP, K_PAGEUP) \
173  X(SDL_SCANCODE_PAGEDOWN, K_PAGEDOWN) \
174  X(SDL_SCANCODE_1, K_1) \
175  X(SDL_SCANCODE_2, K_2) \
176  X(SDL_SCANCODE_3, K_3) \
177  X(SDL_SCANCODE_4, K_4) \
178  X(SDL_SCANCODE_5, K_5) \
179  X(SDL_SCANCODE_6, K_6) \
180  X(SDL_SCANCODE_7, K_7) \
181  X(SDL_SCANCODE_8, K_8) \
182  X(SDL_SCANCODE_9, K_9) \
183  X(SDL_SCANCODE_0, K_0) \
184  X(SDL_SCANCODE_A, K_A) \
185  X(SDL_SCANCODE_B, K_B) \
186  X(SDL_SCANCODE_C, K_C) \
187  X(SDL_SCANCODE_D, K_D) \
188  X(SDL_SCANCODE_E, K_E) \
189  X(SDL_SCANCODE_F, K_F) \
190  X(SDL_SCANCODE_G, K_G) \
191  X(SDL_SCANCODE_H, K_H) \
192  X(SDL_SCANCODE_I, K_I) \
193  X(SDL_SCANCODE_J, K_J) \
194  X(SDL_SCANCODE_K, K_K) \
195  X(SDL_SCANCODE_L, K_L) \
196  X(SDL_SCANCODE_M, K_M) \
197  X(SDL_SCANCODE_N, K_N) \
198  X(SDL_SCANCODE_O, K_O) \
199  X(SDL_SCANCODE_P, K_P) \
200  X(SDL_SCANCODE_Q, K_Q) \
201  X(SDL_SCANCODE_R, K_R) \
202  X(SDL_SCANCODE_S, K_S) \
203  X(SDL_SCANCODE_T, K_T) \
204  X(SDL_SCANCODE_U, K_U) \
205  X(SDL_SCANCODE_V, K_V) \
206  X(SDL_SCANCODE_W, K_W) \
207  X(SDL_SCANCODE_X, K_X) \
208  X(SDL_SCANCODE_Y, K_Y) \
209  X(SDL_SCANCODE_Z, K_Z) \
210  X(SDL_SCANCODE_MINUS, K_MINUS) \
211  X(SDL_SCANCODE_EQUALS, K_EQUAL) \
212  X(SDL_SCANCODE_LEFTBRACKET, K_LEFT_BRACKET) \
213  X(SDL_SCANCODE_RIGHTBRACKET, K_RIGHT_BRACKET) \
214  X(SDL_SCANCODE_SEMICOLON, K_SEMICOLON) \
215  X(SDL_SCANCODE_APOSTROPHE, K_APOSTROPHE) \
216  X(SDL_SCANCODE_GRAVE, K_GRAVE_ACCENT) \
217  X(SDL_SCANCODE_BACKSLASH, K_BACKSLASH) \
218  X(SDL_SCANCODE_COMMA, K_COMMA) \
219  X(SDL_SCANCODE_PERIOD, K_PERIOD) \
220  X(SDL_SCANCODE_SLASH, K_SLASH) \
221  X(SDL_SCANCODE_CAPSLOCK, K_CAPS) \
222  X(SDL_SCANCODE_NUMLOCKCLEAR, K_NUM) \
223  X(SDL_SCANCODE_KP_7, K_NUM7) \
224  X(SDL_SCANCODE_KP_8, K_NUM8) \
225  X(SDL_SCANCODE_KP_9, K_NUM9) \
226  X(SDL_SCANCODE_KP_4, K_NUM4) \
227  X(SDL_SCANCODE_KP_5, K_NUM5) \
228  X(SDL_SCANCODE_KP_6, K_NUM6) \
229  X(SDL_SCANCODE_KP_1, K_NUM1) \
230  X(SDL_SCANCODE_KP_2, K_NUM2) \
231  X(SDL_SCANCODE_KP_3, K_NUM3) \
232  X(SDL_SCANCODE_KP_0, K_NUM0) \
233  X(SDL_SCANCODE_KP_PERIOD, K_DECIMAL) \
234  X(SDL_SCANCODE_NONUSBACKSLASH, K_86) \
235  X(SDL_SCANCODE_KP_ENTER, K_NUM_RETURN) \
236  X(SDL_SCANCODE_KP_DIVIDE, K_DIVIDE) \
237  X(SDL_SCANCODE_LGUI, K_WIN_L) \
238  X(SDL_SCANCODE_RGUI, K_WIN_R) \
239  X(SDL_SCANCODE_PRINTSCREEN, K_PRINT) \
240 
241 static C4KeyCode sdl_scancode_to_keycode(SDL_Scancode scancode)
242 {
243  switch (scancode)
244  {
245 #define X(sdl, oc) case sdl: return oc;
247 #undef X
248  default: return 0; // silence warnings.
249  }
250 }
251 
252 const char* KeycodeToString(C4KeyCode code)
253 {
254  SDL_Scancode scancode;
255  switch (code)
256  {
257 #define X(sdl, oc) case oc: scancode = sdl; break;
259 #undef X
260  default:
261  return nullptr;
262  }
263  return SDL_GetScancodeName(scancode);
264 }
265 
266 void C4AbstractApp::HandleSDLEvent(SDL_Event& e)
267 {
268  DWORD flags;
269  // Directly handle QUIT messages.
270  switch (e.type)
271  {
272  case SDL_QUIT:
273  Quit();
274  break;
275  case SDL_TEXTINPUT:
276  ::pGUI->CharIn(e.text.text);
277  break;
278  case SDL_KEYDOWN:
279  {
280  Game.DoKeyboardInput(sdl_scancode_to_keycode(e.key.keysym.scancode), KEYEV_Down,
281  e.key.keysym.mod & (KMOD_LALT | KMOD_RALT),
282  e.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL),
283  e.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT),
284  e.key.repeat > 0, nullptr);
285  break;
286  }
287  case SDL_KEYUP:
288  Game.DoKeyboardInput(sdl_scancode_to_keycode(e.key.keysym.scancode), KEYEV_Up,
289  e.key.keysym.mod & (KMOD_LALT | KMOD_RALT),
290  e.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL),
291  e.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT), false, nullptr);
292  break;
293  case SDL_MOUSEMOTION:
294  C4GUI::MouseMove(C4MC_Button_None, e.motion.x, e.motion.y, 0, nullptr);
295  break;
296  case SDL_MOUSEBUTTONUP:
297  case SDL_MOUSEBUTTONDOWN:
298  int32_t button;
299  sdlToC4MCBtn(e.button, button, flags);
300  C4GUI::MouseMove(button, e.button.x, e.button.y, flags, nullptr);
301  break;
302  case SDL_MOUSEWHEEL:
303  flags = e.wheel.y > 0 ? (+32) << 16 : (DWORD) (-32) << 16;
304  flags += SDL_GetModState();
305  int x, y;
306  SDL_GetMouseState(&x, &y);
307  C4GUI::MouseMove(C4MC_Button_Wheel, x, y, flags, nullptr);
308  break;
309  case SDL_CONTROLLERAXISMOTION:
310  case SDL_CONTROLLERBUTTONDOWN:
311  case SDL_CONTROLLERBUTTONUP:
312  Application.pGamePadControl->FeedEvent(e, C4GamePadControl::FEED_BUTTONS);
313  break;
314  case SDL_JOYDEVICEADDED:
315  case SDL_CONTROLLERDEVICEADDED:
316  case SDL_CONTROLLERDEVICEREMOVED:
317  Application.pGamePadControl->CheckGamePad(e);
318  break;
319  case SDL_WINDOWEVENT:
320  // Forward to C4Window instance.
321  auto window = static_cast<C4Window*>(SDL_GetWindowData(SDL_GetWindowFromID(e.window.windowID), "C4Window"));
322  window->HandleSDLEvent(e.window);
323  break;
324  }
325 }
326 
327 static int modeCount = 0;
328 
329 static int bits_per_pixel(int format)
330 {
331  // C4Draw::BITS_PER_PIXEL is 32, and other parts of the code expect
332  // the mode's bpp to match exactly. 24 is fully compatible, so just
333  // pretend it's 32 in that case to adhere to the expected interface.
334  int bbp = SDL_BITSPERPIXEL(format);
335  if (bbp == 24) bbp = 32;
336  return bbp;
337 }
338 
339 bool C4AbstractApp::GetIndexedDisplayMode(int32_t iIndex, int32_t *piXRes, int32_t *piYRes, int32_t *piBitDepth, int32_t *piRefreshRate, uint32_t iMonitor)
340 {
341  if (!modeCount)
342  {
343  modeCount = SDL_GetNumDisplayModes(iMonitor);
344  }
345 
346  if (iIndex >= modeCount)
347  return false;
348 
349  SDL_DisplayMode mode;
350  SDL_GetDisplayMode(iMonitor, iIndex, &mode);
351  *piXRes = mode.w;
352  *piYRes = mode.h;
353  *piBitDepth = bits_per_pixel(mode.format);
354  if (piRefreshRate) *piRefreshRate = mode.refresh_rate;
355  return true;
356 }
357 
358 bool C4AbstractApp::SetVideoMode(int iXRes, int iYRes, unsigned int RefreshRate, unsigned int iMonitor, bool fFullScreen)
359 {
360  int res;
361  if (!fFullScreen)
362  {
363  if (iXRes == -1)
364  {
365  SDL_DisplayMode desktop_mode;
366  res = SDL_GetDesktopDisplayMode(iMonitor, &desktop_mode);
367  if (res)
368  {
369  Error(SDL_GetError());
370  LogF("SDL_GetDesktopDisplayMode: %s", SDL_GetError());
371  return false;
372  }
373 
374  iXRes = desktop_mode.w;
375  iYRes = desktop_mode.h;
376  }
377 
378  res = SDL_SetWindowFullscreen(pWindow->window, 0);
379  if (res)
380  {
381  Error(SDL_GetError());
382  LogF("SDL_SetWindowFullscreen: %s", SDL_GetError());
383  return false;
384  }
385 
386  pWindow->SetSize(iXRes, iYRes);
387  OnResolutionChanged(iXRes, iYRes);
388  return true;
389  }
390  SDL_DisplayMode mode;
391  if (iXRes < 0 || iYRes < 0)
392  {
393  res = SDL_SetWindowFullscreen(pWindow->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
394  if (res)
395  {
396  Error(SDL_GetError());
397  LogF("SDL_SetWindowFullscreen: %s", SDL_GetError());
398  return false;
399  }
400  res = SDL_GetDesktopDisplayMode(iMonitor, &mode);
401  if (res)
402  {
403  Error(SDL_GetError());
404  LogF("SDL_GetDesktopDisplayMode: %s", SDL_GetError());
405  return false;
406  }
407  OnResolutionChanged(mode.w, mode.h);
408  return true;
409  }
410 
411  for (int i = 0; i < modeCount; ++i)
412  {
413  res = SDL_GetDisplayMode(iMonitor, i, &mode);
414 
415  if (res)
416  {
417  Error(SDL_GetError());
418  LogF("SDL_GetDisplayMode: %s", SDL_GetError());
419  return false;
420  }
421 
422  if (mode.w == iXRes && mode.h == iYRes && (RefreshRate == 0 || mode.refresh_rate == RefreshRate) && bits_per_pixel(mode.format) == C4Draw::COLOR_DEPTH)
423  {
424  res = SDL_SetWindowDisplayMode(pWindow->window, &mode);
425  if (res)
426  {
427  Error(SDL_GetError());
428  LogF("SDL_SetWindowDisplayMode: %s", SDL_GetError());
429  return false;
430  }
431  res = SDL_SetWindowFullscreen(pWindow->window, SDL_WINDOW_FULLSCREEN);
432  if (res)
433  {
434  Error(SDL_GetError());
435  LogF("SDL_SetWindowFullscreen: %s", SDL_GetError());
436  return false;
437  }
438  OnResolutionChanged(mode.w, mode.h);
439  return true;
440  }
441  }
442 
443  Error("No such resolution available");
444  return false;
445 }
446 
448 {
449  if (pWindow && pWindow->window)
450  SDL_SetWindowFullscreen(pWindow->window, 0);
451 }
452 
453 bool C4AbstractApp::Copy(const std::string &text, bool fClipboard)
454 {
455  return SDL_SetClipboardText(text.c_str()) == 0;
456 }
457 
458 std::string C4AbstractApp::Paste(bool fClipboard)
459 {
460  char * text = SDL_GetClipboardText();
461  std::string buf(text);
462  SDL_free(text);
463  return buf;
464 }
465 
466 bool C4AbstractApp::IsClipboardFull(bool fClipboard)
467 {
468  return SDL_HasClipboardText();
469 }
470 
471 void C4AbstractApp::MessageDialog(const char * message)
472 {
473  SDL_ShowSimpleMessageBox(0, C4ENGINECAPTION, message, pWindow ? pWindow->window : 0);
474 }
const int32_t C4MC_Button_MiddleUp
virtual void OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)=0
const int32_t C4MC_Button_LeftDown
C4Game Game
Definition: C4Globals.cpp:52
void MessageDialog(const char *message)
Definition: C4AppMac.mm:63
static constexpr int COLOR_DEPTH
Definition: C4Draw.h:89
void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP)
Definition: C4Gui.h:2829
bool SetVideoMode(int iXRes, int iYRes, unsigned int iRefreshRate, unsigned int iMonitor, bool fFullScreen)
Definition: C4AppSDL.cpp:358
const int32_t C4MC_Button_RightUp
const int32_t C4MC_Button_X2Down
const int32_t C4MC_Button_MiddleDouble
const char * KeycodeToString(C4KeyCode code)
Definition: C4AppSDL.cpp:252
const int32_t C4MC_Button_X1Double
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 int32_t C4MC_Button_LeftDouble
bool FlushMessages()
Definition: C4AppSDL.cpp:119
void SetSize(unsigned int cx, unsigned int cy)
Definition: C4AppT.cpp:111
const int32_t C4MC_Button_X2Double
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1194
virtual ~C4AbstractApp()
Definition: C4AppSDL.cpp:87
const int32_t C4MC_Button_LeftUp
const int32_t C4MC_Button_X1Up
virtual bool DoInit(int argc, char *argv[])=0
void Error(const char *m)
Definition: C4App.h:99
const int32_t C4MC_Button_X1Down
const int32_t C4MC_Button_RightDown
bool fQuitMsgReceived
Definition: C4App.h:81
virtual void Quit()
Definition: C4AppSDL.cpp:114
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
bool Copy(const std::string &text, bool fClipboard=true)
Definition: C4AppMac.mm:34
void RestoreVideoMode()
Definition: C4AppSDL.cpp:447
bool Init(int argc, char *argv[])
Definition: C4AppSDL.cpp:91
virtual void Clear()
Definition: C4AppSDL.cpp:109
const int32_t C4MC_Button_RightDouble
const int32_t C4MC_Button_None
std::string Paste(bool fClipboard=true)
Definition: C4AppMac.mm:47
#define SDL_SCANCODE_KEYCODE
Definition: C4AppSDL.cpp:134
const int32_t C4MC_Button_MiddleDown
C4GamePadControl * pGamePadControl
Definition: C4Application.h:43
uint32_t DWORD
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
C4Window * pWindow
Definition: C4App.h:80
unsigned long C4KeyCode
C4Application Application
Definition: C4Globals.cpp:44
bool IsClipboardFull(bool fClipboard=true)
Definition: C4AppMac.mm:58
const int32_t C4MC_Button_X2Up