1 namespace gui::drivers;
5 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__DOS__)
7 #define DBLCLICK_DELAY 0.3 // seconds
8 #define DBLCLICK_DELTA 1
10 // #define DEBUG_THREADS
12 #define bool CursesBool
28 static class Message : struct
36 static OldList messages;
38 static Window nCursesDesktop;
39 static Point mousePosition;
40 static Box mouseRange;
41 static int caretX, caretY, caretVisible;
43 #define TIMEOUT (1000 / 18.2)
45 static Key characters2Key[256] =
48 0,0,0,0,0,0,0,0,backSpace,tab,0,0,0,enter,0,0,
49 leftShift,leftControl,0,0,capsLock,0,0,0,0,0,0,escape,0,0,0,0,
50 // space,pageUp,pageDown,end,home,left,up,right,down,keyPad5,0,0,0,insert,period,0,
51 space,k1,quote,k3,k4,k5,k7,quote,k9,k0,k8,equal,comma,minus,period,slash,
52 k0,k1,k2,k3,k4,k5,k6,k7,k8,k9,semicolon,semicolon,comma,equal,period,slash,
53 k2,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
54 p,q,r,s,t,u,v,w,x,y,z,leftBracket,slash,rightBracket,k6,minus,
55 quote,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
56 p,q,r,s,t,u,v,w,x,y,z,leftBracket,backSlash,rightBracket,quote,backSpace
59 static char * clipBoardData = null;
61 void ClearClipboard();
63 static Thread ncursesThread;
64 static Mutex ncursesMutex;
65 static bool ncursesTerminate;
67 /****************************************************************************
68 /// PRIVATE UTILITIES /////////////
69 ****************************************************************************/
71 static void AddMsg(uint vmid, int x, int y, Key key)
73 messages.Add(Message { vmid = vmid, x = x, y = y, key = key });
77 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
78 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
79 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
80 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
81 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
82 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
83 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
84 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
85 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
86 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
87 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
88 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
89 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
90 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
93 static void AddMessagesToQueue(int ch)
98 #ifdef NCURSES_VERSION
103 for(; ch != ERR; ch = getch())
105 if(ch == 0x1B) //Escaped = true;
109 fprintf(f, " \\e: %d\n",ch);
111 if((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
113 key = characters2Key[ch];
120 fprintf(f, " [: %d\n",ch);
124 fprintf(f, " MOD: %c\n",ch1);
128 if(ch1 == 'A') key = f1;
129 else if(ch1 == 'B') key = f2;
130 else if(ch1 == 'C') key = f3;
131 else if(ch1 == 'D') key = f4;
132 else if(ch1 == 'E') key = f5;
136 if(ch1 == '$') keyFlags.shift = true;
137 else if(ch1 == '^') keyFlags.ctrl = true;
138 else if(ch1 == '@') { keyFlags.shift = true; keyFlags.ctrl = true; }
144 case '1': case '7': key = home; break;
145 case '2': key = insert; break;
146 case '3': key = del; break;
147 case '4': case '8': key = end; break;
148 case '5': key = pageUp; break;
149 case '6': key = pageDown; break;
150 case 'a': keyFlags.shift = true; case 'A': key = up; break;
151 case 'b': keyFlags.shift = true; case 'B': key = down; break;
152 case 'c': keyFlags.shift = true; case 'C': key = right; break;
153 case 'd': keyFlags.shift = true; case 'D': key = left; break;
154 case 'k': keyFlags.shift = true; key = tab; break;
163 fprintf(f, " O: %d\n",ch);
165 keyFlags.ctrl = true;
168 case 'a': key = up; break;
169 case 'b': key = down; break;
170 case 'c': key = right; break;
171 case 'd': key = left; break;
192 key = characters2Key[ch];
195 keyFlags.ctrl = true;
196 key = characters2Key[ch + 'a' - 1];
204 case KEY_ENTER: key = enter; break;
205 case KEY_BACKSPACE: key = backSpace; break;
206 case KEY_LEFT: key = left; break;
207 case KEY_RIGHT: key = right; break;
208 case KEY_UP: key = up; break;
209 case KEY_DOWN: key = down; break;
210 case KEY_SLEFT: key = left; keyFlags.shift = true; break;
211 case KEY_SRIGHT: key = right; keyFlags.shift = true; break;
212 // case KEY_SUP: key = up; keyFlags.shift = true; break;
213 // case KEY_SDOWN: key = down; keyFlags.shift = true; break;
214 case 353: key = tab; keyFlags.shift = true; break;
216 keyFlags.shift = true;
217 case KEY_DC: key = del; break;
218 case KEY_IC: case KEY_EIC: key = insert; break;
220 case KEY_HOME: key = home; break;
222 keyFlags.ctrl = true;
224 case KEY_END: key = end; break;
225 case KEY_SHOME: key = home; keyFlags.shift = true; break;
226 case KEY_SEND: key = end; keyFlags.shift = true; break;
227 case KEY_NPAGE: key = pageDown; break;
228 case KEY_PPAGE: key = pageUp; break;
230 // NCurses Extensions
231 #ifdef NCURSES_VERSION
233 resizeterm(LINES, COLS);
234 guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, true);
238 static double lastTime[3];
239 static Point lastPos[3];
242 x = Min(Max(event.x * textCellW, mouseRange.left), mouseRange.right);
243 y = Min(Max(event.y * textCellH, mouseRange.top), mouseRange.bottom);
244 if(mousePosition.x != x || mousePosition.y != y)
249 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, key);
251 if(event.bstate & BUTTON1_PRESSED)
253 Time time = GetTime();
254 if(time - lastTime[0] < DBLCLICK_DELAY &&
255 Abs(mousePosition.x - lastPos[0].x) < DBLCLICK_DELTA &&
256 Abs(mousePosition.y - lastPos[0].y) < DBLCLICK_DELTA)
257 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, keyFlags);
259 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, keyFlags);
261 lastPos[0] = mousePosition;
263 if(event.bstate & BUTTON1_RELEASED)
264 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, keyFlags);
265 if(event.bstate & BUTTON3_PRESSED)
267 Time time = GetTime();
268 if(time - lastTime[2] < DBLCLICK_DELAY &&
269 Abs(mousePosition.x - lastPos[2].x) < DBLCLICK_DELTA &&
270 Abs(mousePosition.y - lastPos[2].y) < DBLCLICK_DELTA)
271 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick, x, y, keyFlags);
273 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, x, y, keyFlags);
275 lastPos[2] = mousePosition;
277 if(event.bstate & BUTTON3_RELEASED)
278 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp, x, y, keyFlags);
279 if(event.bstate & BUTTON2_PRESSED)
281 Time time = GetTime();
282 if(time - lastTime[1] < DBLCLICK_DELAY &&
283 Abs(mousePosition.x - lastPos[1].x) < DBLCLICK_DELTA &&
284 Abs(mousePosition.y - lastPos[1].y) < DBLCLICK_DELTA)
285 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick, x, y, keyFlags);
287 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, x, y, keyFlags);
289 lastPos[1] = mousePosition;
291 if(event.bstate & BUTTON2_RELEASED)
292 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp, x, y, keyFlags);
297 if(ch >= KEY_F(1) && ch <= KEY_F(12))
298 key = (Key)f1 + ch-KEY_F(1);
299 if(ch >= KEY_F(13) && ch <= KEY_F(24))
301 key = (Key)f1 + ch-KEY_F(13);
302 keyFlags.shift = true;
309 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, ch, 1, key);
310 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, ch, 0, key);
315 static uint NCursesThread(Thread thread)
317 while(!ncursesTerminate)
324 AddMessagesToQueue(ch);
325 guiApp.SignalEvent();
327 ncursesMutex.Release();
329 Sleep(TIMEOUT / 1000.0);
334 /****************************************************************************
335 /// DRIVER IMPLEMENTATION /////////////
336 ****************************************************************************/
337 class NCursesInterface : Interface
339 class_property(name) = "NCurses";
341 // --- User Interface System ---
344 static byte colorMap[8] = {0,4,2,6,1,5,3,7};
349 SetLoggingMode(buffer, null);
350 if((bool)has_colors())
355 init_pair(c++,colorMap[f],colorMap[b]);
358 printf( "\033(U\017");
360 intrflush(stdscr, (_Bool)false);
363 keypad(stdscr, (_Bool)true);
364 #ifdef NCURSES_VERSION
366 mousemask(REPORT_MOUSE_POSITION |
367 BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_DOUBLE_CLICKED |
368 BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_DOUBLE_CLICKED |
369 BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_DOUBLE_CLICKED, null);
372 idlok(stdscr, (_Bool)false);
373 idcok(stdscr, (_Bool)false);
374 clearok(stdscr, (_Bool)false);
375 scrollok(stdscr, (_Bool)false);
377 caretVisible = false;
378 leaveok(stdscr, (_Bool)true);
382 ncursesMutex = Mutex { };
384 ncursesTerminate = false;
386 ncursesThread = Thread { };
387 incref ncursesThread;
388 ncursesThread.Main = NCursesThread;
389 ncursesThread.Create();
397 ncursesTerminate = true;
398 ncursesMutex.Release();
399 ncursesThread.Wait();
400 delete ncursesThread;
406 bool ProcessInput(bool processAll)
412 while(!ncursesTerminate && (msg = messages.first))
415 messages.Remove(msg);
416 if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown || msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp)
417 nCursesDesktop.KeyMessage(msg.vmid, msg.key, (char)msg.x);
420 if(nCursesDesktop.MouseMessage(msg.vmid, msg.x, msg.y, (Modifiers *)&msg.key, false, true))
422 if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick)
423 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
424 else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick)
425 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
426 else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick)
427 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
432 if(processAll && ncursesMutex)
434 ncursesMutex.Release();
441 else if(caretVisible)
443 leaveok(stdscr, (_Bool)false);
453 ncursesMutex.Release();
458 void Lock(Window window)
463 void Unlock(Window window)
468 char ** GraphicsDrivers(int * numDrivers)
470 static char *graphicsDrivers[] = { "NCurses" };
471 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
472 return (char **)graphicsDrivers;
475 void GetCurrentMode(bool * fullScreen, Resolution * resolution, PixelFormat * colorDepth, int * refreshRate)
478 *colorDepth = pixelFormatText;
481 void EnsureFullScreen(bool *fullScreen)
486 void OffsetWindow(Window window, int * x, int * y)
491 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
495 guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, false);
497 mouseRange.right = MAXINT;
498 mouseRange.bottom = MAXINT;
504 // --- Window Creation ---
505 void * CreateRootWindow(Window window)
507 nCursesDesktop = window;
508 return (void *) stdscr;
511 void DestroyRootWindow(void * windowHandle)
515 // -- Window manipulation ---
517 void SetRootWindowCaption(void * windowHandle, char * name)
521 void PositionRootWindow(void * windowHandle, int x, int y, int w, int h, bool move, bool resize)
525 void OrderRootWindow(void * windowHandle, bool topMost)
529 void SetRootWindowColor(Window window)
533 void UpdateRootWindow(void * windowHandle)
537 void SetRootWindowState(Window window, int state, bool visible)
542 void ActivateRootWindow(void * windowHandle)
546 // --- Mouse-based window movement ---
548 void StartMoving(void * windowHandle, int x, int y, bool fromKeyBoard)
552 void StopMoving(void * windowHandle)
556 // -- Mouse manipulation ---
558 void GetMousePosition(int *x, int *y)
560 *x = mousePosition.x;
561 *y = mousePosition.y;
564 void SetMousePosition(int x, int y)
570 void SetMouseRange(Window window, Box box)
578 mouseRange.left = mouseRange.top = 0;
579 mouseRange.right = mouseRange.bottom = MAXINT;
583 void SetMouseCapture(void * windowHandle)
588 // -- Mouse cursor ---
590 void SetMouseCursor(int cursor)
597 void SetCaret(int x, int y, int size)
601 caretX = x / textCellW;
602 caretY = y / textCellH;
606 caretVisible = false;
609 // --- Clipboard manipulation ---
611 void ClearClipboard()
613 delete clipBoardData;
616 bool AllocateClipboard(ClipBoard clipBoard, uint size)
619 if((clipBoard.text = new char[size]))
624 bool SaveClipboard(ClipBoard clipBoard)
629 delete clipBoardData;
630 clipBoardData = clipBoard.text;
631 clipBoard.text = null;
637 bool LoadClipboard(ClipBoard clipBoard)
642 clipBoard.text = clipBoardData;
648 void UnloadClipboard(ClipBoard clipBoard)
653 // --- State based input ---
655 bool AcquireInput(void * windowHandle, bool state)
660 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
667 bool GetJoystickState(int device, Joystick joystick)
674 bool GetKeyState(Key key)