1 namespace gui::drivers;
5 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__DOS__)
8 #define DBLCLICK_DELAY 0.3 // seconds
9 #define DBLCLICK_DELTA 1
11 // #define DEBUG_THREADS
21 static class Message : struct
29 static OldList messages;
31 static Window nCursesDesktop;
32 static Point mousePosition;
33 static Box mouseRange;
34 static int caretX, caretY, caretVisible;
36 #define TIMEOUT (1000 / 18.2)
38 static Key characters2Key[256] =
41 0,0,0,0,0,0,0,0,backSpace,tab,0,0,0,enter,0,0,
42 leftShift,leftControl,0,0,capsLock,0,0,0,0,0,0,escape,0,0,0,0,
43 // space,pageUp,pageDown,end,home,left,up,right,down,keyPad5,0,0,0,insert,period,0,
44 space,k1,quote,k3,k4,k5,k7,quote,k9,k0,k8,equal,comma,minus,period,slash,
45 k0,k1,k2,k3,k4,k5,k6,k7,k8,k9,semicolon,semicolon,comma,equal,period,slash,
46 k2,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
47 p,q,r,s,t,u,v,w,x,y,z,leftBracket,slash,rightBracket,k6,minus,
48 quote,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
49 p,q,r,s,t,u,v,w,x,y,z,leftBracket,backSlash,rightBracket,quote,backSpace
52 static char * clipBoardData = null;
54 void ClearClipboard();
56 static Thread ncursesThread;
57 static Mutex ncursesMutex;
58 static bool ncursesTerminate;
60 /****************************************************************************
61 /// PRIVATE UTILITIES /////////////
62 ****************************************************************************/
64 static void AddMsg(uint vmid, int x, int y, Key key)
66 messages.Add(Message { vmid = vmid, x = x, y = y, key = key });
70 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
71 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
72 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
73 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
74 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
75 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
76 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
77 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
78 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
79 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
80 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
81 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
82 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
83 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
86 static void AddMessagesToQueue(int ch)
91 #ifdef NCURSES_VERSION
96 for(; ch != ERR; ch = getch())
98 if(ch == 0x1B) //Escaped = true;
102 fprintf(f, " \\e: %d\n",ch);
104 if((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
106 key = characters2Key[ch];
113 fprintf(f, " [: %d\n",ch);
117 fprintf(f, " MOD: %c\n",ch1);
121 if(ch1 == 'A') key = f1;
122 else if(ch1 == 'B') key = f2;
123 else if(ch1 == 'C') key = f3;
124 else if(ch1 == 'D') key = f4;
125 else if(ch1 == 'E') key = f5;
129 if(ch1 == '$') keyFlags.shift = true;
130 else if(ch1 == '^') keyFlags.ctrl = true;
131 else if(ch1 == '@') { keyFlags.shift = true; keyFlags.ctrl = true; }
137 case '1': case '7': key = home; break;
138 case '2': key = insert; break;
139 case '3': key = del; break;
140 case '4': case '8': key = end; break;
141 case '5': key = pageUp; break;
142 case '6': key = pageDown; break;
143 case 'a': keyFlags.shift = true; case 'A': key = up; break;
144 case 'b': keyFlags.shift = true; case 'B': key = down; break;
145 case 'c': keyFlags.shift = true; case 'C': key = right; break;
146 case 'd': keyFlags.shift = true; case 'D': key = left; break;
147 case 'k': keyFlags.shift = true; key = tab; break;
156 fprintf(f, " O: %d\n",ch);
158 keyFlags.ctrl = true;
161 case 'a': key = up; break;
162 case 'b': key = down; break;
163 case 'c': key = right; break;
164 case 'd': key = left; break;
185 key = characters2Key[ch];
188 keyFlags.ctrl = true;
189 key = characters2Key[ch + 'a' - 1];
197 case KEY_ENTER: key = enter; break;
198 case KEY_BACKSPACE: key = backSpace; break;
199 case KEY_LEFT: key = left; break;
200 case KEY_RIGHT: key = right; break;
201 case KEY_UP: key = up; break;
202 case KEY_DOWN: key = down; break;
203 case KEY_SLEFT: key = left; keyFlags.shift = true; break;
204 case KEY_SRIGHT: key = right; keyFlags.shift = true; break;
205 // case KEY_SUP: key = up; keyFlags.shift = true; break;
206 // case KEY_SDOWN: key = down; keyFlags.shift = true; break;
207 case 353: key = tab; keyFlags.shift = true; break;
209 keyFlags.shift = true;
210 case KEY_DC: key = del; break;
211 case KEY_IC: case KEY_EIC: key = insert; break;
213 case KEY_HOME: key = home; break;
215 keyFlags.ctrl = true;
217 case KEY_END: key = end; break;
218 case KEY_SHOME: key = home; keyFlags.shift = true; break;
219 case KEY_SEND: key = end; keyFlags.shift = true; break;
220 case KEY_NPAGE: key = pageDown; break;
221 case KEY_PPAGE: key = pageUp; break;
223 // NCurses Extensions
224 #ifdef NCURSES_VERSION
226 resizeterm(LINES, COLS);
227 guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, true);
231 static double lastTime[3];
232 static Point lastPos[3];
235 x = Min(Max(event.x * textCellW, mouseRange.left), mouseRange.right);
236 y = Min(Max(event.y * textCellH, mouseRange.top), mouseRange.bottom);
237 if(mousePosition.x != x || mousePosition.y != y)
242 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, key);
244 if(event.bstate & BUTTON1_PRESSED)
246 Time time = GetTime();
247 if(time - lastTime[0] < DBLCLICK_DELAY &&
248 Abs(mousePosition.x - lastPos[0].x) < DBLCLICK_DELTA &&
249 Abs(mousePosition.y - lastPos[0].y) < DBLCLICK_DELTA)
250 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, keyFlags);
252 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, keyFlags);
254 lastPos[0] = mousePosition;
256 if(event.bstate & BUTTON1_RELEASED)
257 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, keyFlags);
258 if(event.bstate & BUTTON3_PRESSED)
260 Time time = GetTime();
261 if(time - lastTime[2] < DBLCLICK_DELAY &&
262 Abs(mousePosition.x - lastPos[2].x) < DBLCLICK_DELTA &&
263 Abs(mousePosition.y - lastPos[2].y) < DBLCLICK_DELTA)
264 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick, x, y, keyFlags);
266 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, x, y, keyFlags);
268 lastPos[2] = mousePosition;
270 if(event.bstate & BUTTON3_RELEASED)
271 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp, x, y, keyFlags);
272 if(event.bstate & BUTTON2_PRESSED)
274 Time time = GetTime();
275 if(time - lastTime[1] < DBLCLICK_DELAY &&
276 Abs(mousePosition.x - lastPos[1].x) < DBLCLICK_DELTA &&
277 Abs(mousePosition.y - lastPos[1].y) < DBLCLICK_DELTA)
278 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick, x, y, keyFlags);
280 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, x, y, keyFlags);
282 lastPos[1] = mousePosition;
284 if(event.bstate & BUTTON2_RELEASED)
285 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp, x, y, keyFlags);
290 if(ch >= KEY_F(1) && ch <= KEY_F(12))
291 key = (Key)f1 + ch-KEY_F(1);
292 if(ch >= KEY_F(13) && ch <= KEY_F(24))
294 key = (Key)f1 + ch-KEY_F(13);
295 keyFlags.shift = true;
302 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, ch, 1, key);
303 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, ch, 0, key);
308 static uint NCursesThread(Thread thread)
310 while(!ncursesTerminate)
317 AddMessagesToQueue(ch);
318 guiApp.SignalEvent();
320 ncursesMutex.Release();
322 Sleep(TIMEOUT / 1000.0);
327 /****************************************************************************
328 /// DRIVER IMPLEMENTATION /////////////
329 ****************************************************************************/
330 class NCursesInterface : Interface
332 class_property(name) = "NCurses";
334 // --- User Interface System ---
337 static byte colorMap[8] = {0,4,2,6,1,5,3,7};
342 SetLoggingMode(buffer, null);
343 if((bool)has_colors())
348 init_pair(c++,colorMap[f],colorMap[b]);
351 printf( "\033(U\017");
353 intrflush(stdscr, false);
356 keypad(stdscr, true);
357 #ifdef NCURSES_VERSION
359 mousemask(REPORT_MOUSE_POSITION |
360 BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_DOUBLE_CLICKED |
361 BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_DOUBLE_CLICKED |
362 BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_DOUBLE_CLICKED, null);
365 idlok(stdscr, false);
366 idcok(stdscr, false);
367 clearok(stdscr, false);
368 scrollok(stdscr, false);
370 caretVisible = false;
371 leaveok(stdscr, true);
375 ncursesMutex = Mutex { };
377 ncursesTerminate = false;
379 ncursesThread = Thread { };
380 incref ncursesThread;
381 ncursesThread.Main = NCursesThread;
382 ncursesThread.Create();
390 ncursesTerminate = true;
391 ncursesMutex.Release();
392 ncursesThread.Wait();
393 delete ncursesThread;
399 bool ProcessInput(bool processAll)
405 while(!ncursesTerminate && (msg = messages.first))
408 messages.Remove(msg);
409 if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown || msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp)
410 nCursesDesktop.KeyMessage(msg.vmid, msg.key, (char)msg.x);
413 if(nCursesDesktop.MouseMessage(msg.vmid, msg.x, msg.y, (Modifiers *)&msg.key, false, true))
415 if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick)
416 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
417 else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick)
418 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
419 else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick)
420 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
425 if(processAll && ncursesMutex)
427 ncursesMutex.Release();
434 else if(caretVisible)
436 leaveok(stdscr, false);
446 ncursesMutex.Release();
451 void Lock(Window window)
456 void Unlock(Window window)
461 char ** GraphicsDrivers(int * numDrivers)
463 static char *graphicsDrivers[] = { "NCurses" };
464 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
465 return (char **)graphicsDrivers;
468 void GetCurrentMode(bool * fullScreen, Resolution * resolution, PixelFormat * colorDepth, int * refreshRate)
471 *colorDepth = pixelFormatText;
474 void EnsureFullScreen(bool *fullScreen)
479 void OffsetWindow(Window window, int * x, int * y)
484 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
488 guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, false);
490 mouseRange.right = MAXINT;
491 mouseRange.bottom = MAXINT;
497 // --- Window Creation ---
498 void * CreateRootWindow(Window window)
500 nCursesDesktop = window;
501 return (void *) stdscr;
504 void DestroyRootWindow(void * windowHandle)
508 // -- Window manipulation ---
510 void SetRootWindowCaption(void * windowHandle, char * name)
514 void PositionRootWindow(void * windowHandle, int x, int y, int w, int h, bool move, bool resize)
518 void OrderRootWindow(void * windowHandle, bool topMost)
522 void SetRootWindowColor(Window window)
526 void UpdateRootWindow(void * windowHandle)
530 void SetRootWindowState(Window window, int state, bool visible)
535 void ActivateRootWindow(void * windowHandle)
539 // --- Mouse-based window movement ---
541 void StartMoving(void * windowHandle, int x, int y, bool fromKeyBoard)
545 void StopMoving(void * windowHandle)
549 // -- Mouse manipulation ---
551 void GetMousePosition(int *x, int *y)
553 *x = mousePosition.x;
554 *y = mousePosition.y;
557 void SetMousePosition(int x, int y)
563 void SetMouseRange(Window window, Box box)
571 mouseRange.left = mouseRange.top = 0;
572 mouseRange.right = mouseRange.bottom = MAXINT;
576 void SetMouseCapture(void * windowHandle)
581 // -- Mouse cursor ---
583 void SetMouseCursor(int cursor)
590 void SetCaret(int x, int y, int size)
594 caretX = x / textCellW;
595 caretY = y / textCellH;
599 caretVisible = false;
602 // --- Clipboard manipulation ---
604 void ClearClipboard()
606 delete clipBoardData;
609 bool AllocateClipboard(ClipBoard clipBoard, uint size)
612 if((clipBoard.text = new char[size]))
617 bool SaveClipboard(ClipBoard clipBoard)
622 delete clipBoardData;
623 clipBoardData = clipBoard.text;
624 clipBoard.text = null;
630 bool LoadClipboard(ClipBoard clipBoard)
635 clipBoard.text = clipBoardData;
641 void UnloadClipboard(ClipBoard clipBoard)
646 // --- State based input ---
648 bool AcquireInput(void * windowHandle, bool state)
653 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
660 bool GetJoystickState(int device, Joystick joystick)
667 bool GetKeyState(Key key)