3 namespace gui::drivers;
7 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__DOS__)
10 #define DBLCLICK_DELAY 0.3 // seconds
11 #define DBLCLICK_DELTA 1
13 // #define DEBUG_THREADS
23 static class Message : struct
31 static OldList messages;
33 static Window nCursesDesktop;
34 static Point mousePosition;
35 static Box mouseRange;
36 static int caretX, caretY, caretVisible;
38 #define TIMEOUT (1000 / 18.2)
40 static Key characters2Key[256] =
43 0,0,0,0,0,0,0,0,backSpace,tab,0,0,0,enter,0,0,
44 leftShift,leftControl,0,0,capsLock,0,0,0,0,0,0,escape,0,0,0,0,
45 // space,pageUp,pageDown,end,home,left,up,right,down,keyPad5,0,0,0,insert,period,0,
46 space,k1,quote,k3,k4,k5,k7,quote,k9,k0,k8,equal,comma,minus,period,slash,
47 k0,k1,k2,k3,k4,k5,k6,k7,k8,k9,semicolon,semicolon,comma,equal,period,slash,
48 k2,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,slash,rightBracket,k6,minus,
50 quote,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
51 p,q,r,s,t,u,v,w,x,y,z,leftBracket,backSlash,rightBracket,quote,backSpace
54 static char * clipBoardData = null;
56 void ClearClipboard();
58 static Thread ncursesThread;
59 static Mutex ncursesMutex;
60 static bool ncursesTerminate;
62 /****************************************************************************
63 /// PRIVATE UTILITIES /////////////
64 ****************************************************************************/
66 static void AddMsg(uint vmid, int x, int y, Key key)
68 messages.Add(Message { vmid = vmid, x = x, y = y, key = key });
72 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
73 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
74 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
75 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
76 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
77 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
78 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
79 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
80 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
81 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
82 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
83 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
84 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
85 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
88 static void AddMessagesToQueue(int ch)
93 #ifdef NCURSES_VERSION
98 for(; ch != ERR; ch = getch())
100 if(ch == 0x1B) //Escaped = true;
104 fprintf(f, " \\e: %d\n",ch);
106 if((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
108 key = characters2Key[ch];
115 fprintf(f, " [: %d\n",ch);
119 fprintf(f, " MOD: %c\n",ch1);
123 if(ch1 == 'A') key = f1;
124 else if(ch1 == 'B') key = f2;
125 else if(ch1 == 'C') key = f3;
126 else if(ch1 == 'D') key = f4;
127 else if(ch1 == 'E') key = f5;
131 if(ch1 == '$') keyFlags.shift = true;
132 else if(ch1 == '^') keyFlags.ctrl = true;
133 else if(ch1 == '@') { keyFlags.shift = true; keyFlags.ctrl = true; }
139 case '1': case '7': key = home; break;
140 case '2': key = insert; break;
141 case '3': key = del; break;
142 case '4': case '8': key = end; break;
143 case '5': key = pageUp; break;
144 case '6': key = pageDown; break;
145 case 'a': keyFlags.shift = true; case 'A': key = up; break;
146 case 'b': keyFlags.shift = true; case 'B': key = down; break;
147 case 'c': keyFlags.shift = true; case 'C': key = right; break;
148 case 'd': keyFlags.shift = true; case 'D': key = left; break;
149 case 'k': keyFlags.shift = true; key = tab; break;
158 fprintf(f, " O: %d\n",ch);
160 keyFlags.ctrl = true;
163 case 'a': key = up; break;
164 case 'b': key = down; break;
165 case 'c': key = right; break;
166 case 'd': key = left; break;
187 key = characters2Key[ch];
190 keyFlags.ctrl = true;
191 key = characters2Key[ch + 'a' - 1];
199 case KEY_ENTER: key = enter; break;
200 case KEY_BACKSPACE: key = backSpace; break;
201 case KEY_LEFT: key = left; break;
202 case KEY_RIGHT: key = right; break;
203 case KEY_UP: key = up; break;
204 case KEY_DOWN: key = down; break;
205 case KEY_SLEFT: key = left; keyFlags.shift = true; break;
206 case KEY_SRIGHT: key = right; keyFlags.shift = true; break;
207 // case KEY_SUP: key = up; keyFlags.shift = true; break;
208 // case KEY_SDOWN: key = down; keyFlags.shift = true; break;
209 case 353: key = tab; keyFlags.shift = true; break;
211 keyFlags.shift = true;
212 case KEY_DC: key = del; break;
213 case KEY_IC: case KEY_EIC: key = insert; break;
215 case KEY_HOME: key = home; break;
217 keyFlags.ctrl = true;
219 case KEY_END: key = end; break;
220 case KEY_SHOME: key = home; keyFlags.shift = true; break;
221 case KEY_SEND: key = end; keyFlags.shift = true; break;
222 case KEY_NPAGE: key = pageDown; break;
223 case KEY_PPAGE: key = pageUp; break;
225 // NCurses Extensions
226 #ifdef NCURSES_VERSION
228 resizeterm(LINES, COLS);
229 guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, true);
233 static double lastTime[3];
234 static Point lastPos[3];
237 x = Min(Max(event.x * textCellW, mouseRange.left), mouseRange.right);
238 y = Min(Max(event.y * textCellH, mouseRange.top), mouseRange.bottom);
239 if(mousePosition.x != x || mousePosition.y != y)
244 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, key);
246 if(event.bstate & BUTTON1_PRESSED)
248 Time time = GetTime();
249 if(time - lastTime[0] < DBLCLICK_DELAY &&
250 Abs(mousePosition.x - lastPos[0].x) < DBLCLICK_DELTA &&
251 Abs(mousePosition.y - lastPos[0].y) < DBLCLICK_DELTA)
252 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, keyFlags);
254 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, keyFlags);
256 lastPos[0] = mousePosition;
258 if(event.bstate & BUTTON1_RELEASED)
259 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, keyFlags);
260 if(event.bstate & BUTTON3_PRESSED)
262 Time time = GetTime();
263 if(time - lastTime[2] < DBLCLICK_DELAY &&
264 Abs(mousePosition.x - lastPos[2].x) < DBLCLICK_DELTA &&
265 Abs(mousePosition.y - lastPos[2].y) < DBLCLICK_DELTA)
266 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick, x, y, keyFlags);
268 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, x, y, keyFlags);
270 lastPos[2] = mousePosition;
272 if(event.bstate & BUTTON3_RELEASED)
273 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp, x, y, keyFlags);
274 if(event.bstate & BUTTON2_PRESSED)
276 Time time = GetTime();
277 if(time - lastTime[1] < DBLCLICK_DELAY &&
278 Abs(mousePosition.x - lastPos[1].x) < DBLCLICK_DELTA &&
279 Abs(mousePosition.y - lastPos[1].y) < DBLCLICK_DELTA)
280 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick, x, y, keyFlags);
282 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, x, y, keyFlags);
284 lastPos[1] = mousePosition;
286 if(event.bstate & BUTTON2_RELEASED)
287 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp, x, y, keyFlags);
292 if(ch >= KEY_F(1) && ch <= KEY_F(12))
293 key = (Key)f1 + ch-KEY_F(1);
294 if(ch >= KEY_F(13) && ch <= KEY_F(24))
296 key = (Key)f1 + ch-KEY_F(13);
297 keyFlags.shift = true;
304 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, ch, 1, key);
305 AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, ch, 0, key);
310 static uint NCursesThread(Thread thread)
312 while(!ncursesTerminate)
319 AddMessagesToQueue(ch);
320 guiApp.SignalEvent();
322 ncursesMutex.Release();
324 Sleep(TIMEOUT / 1000.0);
329 /****************************************************************************
330 /// DRIVER IMPLEMENTATION /////////////
331 ****************************************************************************/
332 class NCursesInterface : Interface
334 class_property(name) = "NCurses";
336 // --- User Interface System ---
339 static byte colorMap[8] = {0,4,2,6,1,5,3,7};
344 SetLoggingMode(buffer, null);
345 if((bool)has_colors())
350 init_pair(c++,colorMap[f],colorMap[b]);
353 printf( "\033(U\017");
355 intrflush(stdscr, false);
358 keypad(stdscr, true);
359 #ifdef NCURSES_VERSION
361 mousemask(REPORT_MOUSE_POSITION |
362 BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_DOUBLE_CLICKED |
363 BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_DOUBLE_CLICKED |
364 BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_DOUBLE_CLICKED, null);
367 idlok(stdscr, false);
368 idcok(stdscr, false);
369 clearok(stdscr, false);
370 scrollok(stdscr, false);
372 caretVisible = false;
373 leaveok(stdscr, true);
377 ncursesMutex = Mutex { };
379 ncursesTerminate = false;
381 ncursesThread = Thread { };
382 incref ncursesThread;
383 ncursesThread.Main = NCursesThread;
384 ncursesThread.Create();
392 ncursesTerminate = true;
393 ncursesMutex.Release();
394 ncursesThread.Wait();
395 delete ncursesThread;
401 bool ProcessInput(bool processAll)
407 while(!ncursesTerminate && (msg = messages.first))
410 messages.Remove(msg);
411 if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown || msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp)
412 nCursesDesktop.KeyMessage(msg.vmid, msg.key, (char)msg.x);
415 if(nCursesDesktop.MouseMessage(msg.vmid, msg.x, msg.y, (Modifiers *)&msg.key, false, true))
417 if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick)
418 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
419 else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick)
420 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
421 else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick)
422 nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
427 if(processAll && ncursesMutex)
429 ncursesMutex.Release();
436 else if(caretVisible)
438 leaveok(stdscr, false);
448 ncursesMutex.Release();
453 void Lock(Window window)
458 void Unlock(Window window)
463 const char ** GraphicsDrivers(int * numDrivers)
465 static const char *graphicsDrivers[] = { "NCurses" };
466 *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
467 return (const char **)graphicsDrivers;
470 void GetCurrentMode(bool * fullScreen, Resolution * resolution, PixelFormat * colorDepth, int * refreshRate)
473 *colorDepth = pixelFormatText;
476 void EnsureFullScreen(bool *fullScreen)
481 void OffsetWindow(Window window, int * x, int * y)
486 bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
490 guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, false);
492 mouseRange.right = MAXINT;
493 mouseRange.bottom = MAXINT;
499 // --- Window Creation ---
500 void * CreateRootWindow(Window window)
502 nCursesDesktop = window;
503 return (void *) stdscr;
506 void DestroyRootWindow(void * windowHandle)
510 // -- Window manipulation ---
512 void SetRootWindowCaption(void * windowHandle, const char * name)
516 void PositionRootWindow(void * windowHandle, int x, int y, int w, int h, bool move, bool resize)
520 void OrderRootWindow(void * windowHandle, bool topMost)
524 void SetRootWindowColor(Window window)
528 void UpdateRootWindow(void * windowHandle)
532 void SetRootWindowState(Window window, int state, bool visible)
537 void ActivateRootWindow(void * windowHandle)
541 // --- Mouse-based window movement ---
543 void StartMoving(void * windowHandle, int x, int y, bool fromKeyBoard)
547 void StopMoving(void * windowHandle)
551 // -- Mouse manipulation ---
553 void GetMousePosition(int *x, int *y)
555 *x = mousePosition.x;
556 *y = mousePosition.y;
559 void SetMousePosition(int x, int y)
565 void SetMouseRange(Window window, Box box)
573 mouseRange.left = mouseRange.top = 0;
574 mouseRange.right = mouseRange.bottom = MAXINT;
578 void SetMouseCapture(void * windowHandle)
583 // -- Mouse cursor ---
585 void SetMouseCursor(Window window, int cursor)
592 void SetCaret(int x, int y, int size)
596 caretX = x / textCellW;
597 caretY = y / textCellH;
601 caretVisible = false;
604 // --- Clipboard manipulation ---
606 void ClearClipboard()
608 delete clipBoardData;
611 bool AllocateClipboard(ClipBoard clipBoard, uint size)
614 if((clipBoard.text = new char[size]))
619 bool SaveClipboard(ClipBoard clipBoard)
624 delete clipBoardData;
625 clipBoardData = clipBoard.text;
626 clipBoard.text = null;
632 bool LoadClipboard(ClipBoard clipBoard)
637 clipBoard.text = clipBoardData;
643 void UnloadClipboard(ClipBoard clipBoard)
648 // --- State based input ---
650 bool AcquireInput(void * windowHandle, bool state)
655 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
662 bool GetJoystickState(int device, Joystick joystick)
669 bool GetKeyState(Key key)