ecere:gui/gfx:drivers/cocoa: Partial native Cocoa drivers implementation for Mac...
[sdk] / ecere / src / gui / drivers / NCursesInterface.ec
1 namespace gui::drivers;
2
3 import "instance"
4
5 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__DOS__)
6
7 #undef __BLOCKS__
8 #define DBLCLICK_DELAY  0.3  // seconds
9 #define DBLCLICK_DELTA  1
10
11 // #define DEBUG_THREADS
12
13 #define bool CursesBool
14 #define uint _uint
15
16 #include <curses.h>
17
18 #undef bool
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/time.h>
23 #include <unistd.h>
24
25 import "Interface"
26
27 #undef uint
28
29 static class Message : struct
30 {
31    Message prev, next;
32    uint vmid;
33    int x, y;
34    Key key;
35 };
36
37 static OldList messages;
38
39 static Window nCursesDesktop;
40 static Point mousePosition;
41 static Box mouseRange;
42 static int caretX, caretY, caretVisible;
43
44 #define TIMEOUT (1000 / 18.2)
45
46 static Key characters2Key[256] =
47 {
48 // K_PERIOD - DEL
49    0,0,0,0,0,0,0,0,backSpace,tab,0,0,0,enter,0,0,
50    leftShift,leftControl,0,0,capsLock,0,0,0,0,0,0,escape,0,0,0,0,
51 //   space,pageUp,pageDown,end,home,left,up,right,down,keyPad5,0,0,0,insert,period,0,
52    space,k1,quote,k3,k4,k5,k7,quote,k9,k0,k8,equal,comma,minus,period,slash,
53    k0,k1,k2,k3,k4,k5,k6,k7,k8,k9,semicolon,semicolon,comma,equal,period,slash,
54    k2,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
55    p,q,r,s,t,u,v,w,x,y,z,leftBracket,slash,rightBracket,k6,minus,
56    quote,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,
57    p,q,r,s,t,u,v,w,x,y,z,leftBracket,backSlash,rightBracket,quote,backSpace
58 };
59
60 static char * clipBoardData = null;
61
62 void ClearClipboard();
63
64 static Thread ncursesThread;
65 static Mutex ncursesMutex;
66 static bool ncursesTerminate;
67
68 /****************************************************************************
69    /// PRIVATE UTILITIES /////////////
70 ****************************************************************************/
71
72 static void AddMsg(uint vmid, int x, int y, Key key)
73 {
74    messages.Add(Message { vmid = vmid, x = x, y = y, key = key });
75 }
76
77 default:
78 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
79 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
80 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
81 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
82 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
83 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
84 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
85 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
86 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
87 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
88 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
89 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
90 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
91 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
92 private:
93
94 static void AddMessagesToQueue(int ch)
95 {
96    Key key = 0;
97    Key keyFlags = 0;
98    int ch1;
99 #ifdef NCURSES_VERSION
100    MEVENT event;
101 #endif
102
103    //timeout(0);
104    for(; ch != ERR; ch = getch())
105    {
106       if(ch == 0x1B) //Escaped = true;
107       {
108          ch = getch();
109 #ifdef NCDEBUG            
110          fprintf(f, "  \\e: %d\n",ch);
111 #endif            
112          if((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
113          {
114             key = characters2Key[ch];
115             keyFlags.alt = true;
116          }
117          else if(ch == '[')
118          {
119             ch = getch();
120 #ifdef NCDEBUG
121             fprintf(f, "   [: %d\n",ch);
122 #endif               
123             ch1 = getch();
124 #ifdef NCDEBUG
125             fprintf(f, "   MOD: %c\n",ch1);
126 #endif      
127             if(ch == '[')
128             {
129                if(ch1 == 'A') key = f1;
130                else if(ch1 == 'B') key = f2;
131                else if(ch1 == 'C') key = f3;
132                else if(ch1 == 'D') key = f4;
133                else if(ch1 == 'E') key = f5;
134             }
135             else
136             {
137                if(ch1 == '$') keyFlags.shift = true;
138                else if(ch1 == '^') keyFlags.ctrl = true;
139                else if(ch1 == '@') { keyFlags.shift = true; keyFlags.ctrl = true; }
140                else if(ch1 != ERR)
141                   ungetch(ch1);
142
143                switch(ch)
144                {
145                   case '1': case '7': key = home; break;
146                   case '2': key = insert; break; 
147                   case '3': key = del; break;
148                   case '4': case '8': key = end; break;
149                   case '5': key = pageUp; break;
150                   case '6': key = pageDown; break;
151                   case 'a': keyFlags.shift = true; case 'A': key = up; break;
152                   case 'b': keyFlags.shift = true; case 'B': key = down; break;
153                   case 'c': keyFlags.shift = true; case 'C': key = right; break;
154                   case 'd': keyFlags.shift = true; case 'D': key = left; break;
155                   case 'k': keyFlags.shift = true; key = tab; break;
156                }
157             }
158             ch = 0;
159          }
160          else if(ch == 'O')
161          {
162             ch = getch();
163 #ifdef NCDEBUG
164             fprintf(f, "   O: %d\n",ch);
165 #endif               
166             keyFlags.ctrl = true;
167             switch(ch)
168             {
169                case 'a': key = up; break;
170                case 'b': key = down; break;
171                case 'c': key = right; break;
172                case 'd': key = left; break;
173             }
174             ch = 0;
175          }
176          else
177          {
178             if(ch == ERR)
179             {
180                key = escape;
181                ch = 27;
182             }
183             else 
184                ch = 0;
185             /*else
186             {
187                ungetch(ch);
188             }*/
189          }
190       }
191       else if(ch<256) 
192       {
193          key = characters2Key[ch];
194          if(!key && ch <= 26)
195          {
196             keyFlags.ctrl = true;
197             key = characters2Key[ch + 'a' - 1];
198             ch += 'a' - 1;
199          }
200       }
201       else
202       {
203          switch(ch)
204          {
205             case KEY_ENTER: key = enter; break;
206             case KEY_BACKSPACE: key = backSpace; break;
207             case KEY_LEFT: key = left; break;
208             case KEY_RIGHT: key = right; break;
209             case KEY_UP: key = up; break;
210             case KEY_DOWN: key = down; break;
211             case KEY_SLEFT: key = left; keyFlags.shift = true; break;
212             case KEY_SRIGHT: key = right; keyFlags.shift = true; break;
213 //               case KEY_SUP: key = up; keyFlags.shift = true; break;
214 //               case KEY_SDOWN: key = down; keyFlags.shift = true; break;
215             case 353: key = tab; keyFlags.shift = true; break;
216             case 383:
217                keyFlags.shift = true;
218             case KEY_DC: key = del; break;
219             case KEY_IC: case KEY_EIC: key = insert; break;
220             case 362:
221             case KEY_HOME: key = home; break;
222             case 335:
223                keyFlags.ctrl = true;
224             case 385:
225             case KEY_END: key = end; break;
226             case KEY_SHOME: key = home; keyFlags.shift = true; break;
227             case KEY_SEND: key = end; keyFlags.shift = true; break;
228             case KEY_NPAGE: key = pageDown; break;
229             case KEY_PPAGE: key = pageUp; break;
230
231             // NCurses Extensions
232 #ifdef NCURSES_VERSION
233             case KEY_RESIZE:
234                resizeterm(LINES, COLS);
235                guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, true);
236                break;
237             case KEY_MOUSE:
238             {
239                static double lastTime[3];
240                static Point lastPos[3];
241                int x, y;
242                getmouse(&event);
243                x = Min(Max(event.x * textCellW, mouseRange.left), mouseRange.right);
244                y = Min(Max(event.y * textCellH, mouseRange.top), mouseRange.bottom);
245                if(mousePosition.x != x || mousePosition.y != y)
246                {
247                   mousePosition.x = x;
248                   mousePosition.y = y;
249
250                   AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x, y, key);
251                }
252                if(event.bstate & BUTTON1_PRESSED)
253                {
254                   Time time = GetTime();
255                   if(time - lastTime[0] < DBLCLICK_DELAY && 
256                      Abs(mousePosition.x - lastPos[0].x) < DBLCLICK_DELTA &&
257                      Abs(mousePosition.y - lastPos[0].y) < DBLCLICK_DELTA)
258                      AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x, y, keyFlags);
259                   else
260                      AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, x, y, keyFlags);
261                   lastTime[0] = time;
262                   lastPos[0] = mousePosition;
263                }
264                if(event.bstate & BUTTON1_RELEASED)
265                   AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x, y, keyFlags);
266                if(event.bstate & BUTTON3_PRESSED)
267                {
268                   Time time = GetTime();
269                   if(time - lastTime[2] < DBLCLICK_DELAY && 
270                      Abs(mousePosition.x - lastPos[2].x) < DBLCLICK_DELTA &&
271                      Abs(mousePosition.y - lastPos[2].y) < DBLCLICK_DELTA)
272                      AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick, x, y, keyFlags);
273                   else
274                      AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, x, y, keyFlags);
275                   lastTime[2] = time;
276                   lastPos[2] = mousePosition;
277                }
278                if(event.bstate & BUTTON3_RELEASED)
279                   AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp, x, y, keyFlags);
280                if(event.bstate & BUTTON2_PRESSED)
281                {
282                   Time time = GetTime();
283                   if(time - lastTime[1] < DBLCLICK_DELAY && 
284                      Abs(mousePosition.x - lastPos[1].x) < DBLCLICK_DELTA &&
285                      Abs(mousePosition.y - lastPos[1].y) < DBLCLICK_DELTA)
286                      AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick, x, y, keyFlags);
287                   else
288                      AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, x, y, keyFlags);
289                   lastTime[1] = time;
290                   lastPos[1] = mousePosition;
291                }
292                if(event.bstate & BUTTON2_RELEASED)
293                   AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp,  x, y, keyFlags);
294                break;
295             }
296 #endif
297          }
298          if(ch >= KEY_F(1) && ch <= KEY_F(12))
299             key = (Key)f1 + ch-KEY_F(1);
300          if(ch >= KEY_F(13) && ch <= KEY_F(24))
301          {
302             key = (Key)f1 + ch-KEY_F(13);
303             keyFlags.shift = true;
304          }
305          ch = 0;
306       }
307       if(key)
308       {
309          key |= keyFlags;
310          AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, ch, 1, key);
311          AddMsg(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, ch, 0, key);
312       }
313    }
314 }
315
316 static uint NCursesThread(Thread thread)
317 {
318    while(!ncursesTerminate)
319    {
320       int ch;
321       ncursesMutex.Wait();
322       ch = getch();
323       if(ch != ERR)
324       {
325          AddMessagesToQueue(ch);
326          guiApp.SignalEvent();
327       }
328       ncursesMutex.Release();
329       if(ch == ERR)
330          Sleep(TIMEOUT / 1000.0);
331    }
332    return 0;
333 }
334
335 /****************************************************************************
336    /// DRIVER IMPLEMENTATION /////////////
337 ****************************************************************************/
338 class NCursesInterface : Interface
339 {
340    class_property(name) = "NCurses";
341
342    // --- User Interface System ---
343    bool Initialize()
344    {
345       static byte colorMap[8] = {0,4,2,6,1,5,3,7};
346
347       byte c = 0,f,b;
348       initscr();
349       
350       SetLoggingMode(buffer, null);
351       if((bool)has_colors())
352       {
353          start_color();
354          for(b=0; b<8; b++)
355             for(f=0; f<8; f++)
356                init_pair(c++,colorMap[f],colorMap[b]);
357       }
358      
359       printf( "\033(U\017");
360       fflush(stdout);
361       intrflush(stdscr, (_Bool)false);
362       nonl();
363       curs_set(false);
364       keypad(stdscr, (_Bool)true);
365    #ifdef NCURSES_VERSION
366       ESCDELAY = 0;
367       mousemask(REPORT_MOUSE_POSITION |
368                 BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_DOUBLE_CLICKED |
369                 BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_DOUBLE_CLICKED |
370                 BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_DOUBLE_CLICKED, null);
371       mouseinterval(0);
372    #endif
373       idlok(stdscr, (_Bool)false);
374       idcok(stdscr, (_Bool)false);
375       clearok(stdscr, (_Bool)false);
376       scrollok(stdscr, (_Bool)false);
377       cbreak();
378       caretVisible = false;
379       leaveok(stdscr, (_Bool)true);
380       timeout(0);
381       noecho();
382
383         ncursesMutex = Mutex { };
384
385       ncursesTerminate = false;
386       
387       ncursesThread = Thread { };
388       incref ncursesThread;
389       ncursesThread.Main = NCursesThread;
390       ncursesThread.Create();
391
392       ncursesMutex.Wait();
393       return true;
394    }
395
396    void Terminate()
397    {
398       ncursesTerminate = true;
399       ncursesMutex.Release();
400       ncursesThread.Wait();
401       delete ncursesThread;
402       endwin();
403       delete ncursesMutex;
404       ClearClipboard();
405    }
406
407    bool ProcessInput(bool processAll)
408    {
409       Message msg;
410       bool result = false;
411
412       // Process messages
413       while(!ncursesTerminate && (msg = messages.first))
414       {
415          result = true;
416          messages.Remove(msg);
417          if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown || msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp)
418             nCursesDesktop.KeyMessage(msg.vmid, msg.key, (char)msg.x);
419          else
420          {
421             if(nCursesDesktop.MouseMessage(msg.vmid, msg.x, msg.y, (Modifiers *)&msg.key, false, true))
422             {
423                if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick)
424                   nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
425                else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick)
426                   nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
427                else if(msg.vmid == __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick)
428                   nCursesDesktop.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, msg.x, msg.y, (Modifiers *)&msg.key, false, false);
429             }
430          }
431          delete msg;
432
433          if(processAll && ncursesMutex)
434          {
435             ncursesMutex.Release();
436             ncursesMutex.Wait();
437          }
438          result = true;
439       }
440       if(ncursesTerminate)
441          messages.Free(null);
442       else if(caretVisible)
443       {
444          leaveok(stdscr, (_Bool)false);
445          move(caretY,caretX);
446          refresh();
447          curs_set(true);
448       }
449       return result;
450    }
451
452    void Wait()
453    {
454       ncursesMutex.Release();
455       guiApp.WaitEvent();
456       ncursesMutex.Wait();
457    }
458
459    void Lock(Window window)
460    {
461
462    }
463
464    void Unlock(Window window)
465    {
466
467    }
468
469    char ** GraphicsDrivers(int * numDrivers)
470    {
471       static char *graphicsDrivers[] = { "NCurses" };
472       *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
473       return (char **)graphicsDrivers;
474    }
475
476    void GetCurrentMode(bool * fullScreen, Resolution * resolution, PixelFormat * colorDepth, int * refreshRate)
477    {
478       *fullScreen = true;
479       *colorDepth = pixelFormatText;
480    }
481
482    void EnsureFullScreen(bool *fullScreen)
483    {
484       *fullScreen = true;
485    }
486
487    void OffsetWindow(Window window, int * x, int * y)
488    {
489
490    }
491
492    bool ScreenMode(bool fullScreen, int resolution, int colorDepth, int refreshRate, bool * textMode)
493    {
494       if(fullScreen)
495       {
496          guiApp.SetDesktopPosition(0,0, COLS * textCellW, LINES * textCellH, false);
497          *textMode = true;
498          mouseRange.right = MAXINT;
499          mouseRange.bottom = MAXINT;
500            return true;
501       }
502       return false;
503    }
504
505    // --- Window Creation ---
506    void * CreateRootWindow(Window window)
507    {
508       nCursesDesktop = window;
509       return (void *) stdscr;
510    }
511
512    void DestroyRootWindow(void * windowHandle)
513    {
514    }
515
516    // -- Window manipulation ---
517
518    void SetRootWindowCaption(void * windowHandle, char * name)
519    {
520    }
521
522    void PositionRootWindow(void * windowHandle, int x, int y, int w, int h, bool move, bool resize)
523    {
524    }
525
526    void OrderRootWindow(void * windowHandle, bool topMost)
527    {
528    }
529
530    void SetRootWindowColor(Window window)
531    {
532    }
533
534    void UpdateRootWindow(void * windowHandle)
535    {
536    }
537
538    void SetRootWindowState(Window window, int state, bool visible)
539    {
540
541    }
542
543    void ActivateRootWindow(void * windowHandle)
544    {
545    }
546
547    // --- Mouse-based window movement ---
548
549    void StartMoving(void * windowHandle, int x, int y, bool fromKeyBoard)
550    {
551    }
552
553    void StopMoving(void * windowHandle)
554    {
555    }
556
557    // -- Mouse manipulation ---
558
559    void GetMousePosition(int *x, int *y)
560    {
561       *x = mousePosition.x;
562       *y = mousePosition.y;
563    }
564
565    void SetMousePosition(int x, int y)
566    {
567       mousePosition.x = x;
568       mousePosition.y = y;
569    }
570
571    void SetMouseRange(Window window, Box box)
572    {
573       if(box != null)
574       {
575          mouseRange = box;
576       }
577       else
578       {
579          mouseRange.left = mouseRange.top = 0;
580          mouseRange.right = mouseRange.bottom = MAXINT;
581       }
582    }
583
584    void SetMouseCapture(void * windowHandle)
585    {
586
587    }
588
589    // -- Mouse cursor ---
590
591    void SetMouseCursor(int cursor)
592    {
593
594    }
595
596    // --- Caret ---
597
598    void SetCaret(int x, int y, int size)
599    {
600       if(size)
601       {
602          caretX = x / textCellW;
603          caretY = y / textCellH;
604          caretVisible = true;
605       }
606       else
607          caretVisible = false;
608    }  
609
610    // --- Clipboard manipulation ---
611
612    void ClearClipboard()
613    {
614       delete clipBoardData;
615    }
616
617    bool AllocateClipboard(ClipBoard clipBoard, uint size)
618    {
619       bool result = false;
620       if((clipBoard.text = new char[size]))
621          result = true;   
622       return result;
623    }
624
625    bool SaveClipboard(ClipBoard clipBoard)
626    {
627       bool result = false;
628       if(clipBoard.text)
629       {
630          delete clipBoardData;
631          clipBoardData = clipBoard.text;
632          clipBoard.text = null;
633          result = true;
634       }
635       return result;
636    }
637
638    bool LoadClipboard(ClipBoard clipBoard)
639    {
640       bool result = false;
641       if(clipBoardData)
642       {
643          clipBoard.text = clipBoardData;
644          result = true;
645       }
646       return result;
647    }
648
649    void UnloadClipboard(ClipBoard clipBoard)
650    {
651
652    }
653
654    // --- State based input ---
655
656    bool AcquireInput(void * windowHandle, bool state)
657    {
658       return false;
659    }
660
661    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
662    {
663       bool result = false;
664
665       return result;
666    }
667
668    bool GetJoystickState(int device, Joystick joystick)
669    {
670       bool result = false;
671
672       return result;
673    }
674
675    bool GetKeyState(Key key)
676    {
677       return false;
678    }
679 }
680
681 #endif