1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include <Application.h>
4 #include <Window.h>
5 #include <Rect.h>
6 #include <View.h>
7 #include <Font.h>
8 #include <Bitmap.h>
9 #include <Clipboard.h>
10 #include <Screen.h>
11 #include <Region.h>
12 #include <Beep.h>
13 #include <TranslationUtils.h>
14 #include <Input.h>
15 #include <Alert.h>
16 #include <TextControl.h>
17 #include <LayoutBuilder.h>
18 #include <Button.h>
19 #include <Entry.h>
20 #include <Path.h>
21 
22 #include <pthread.h>
23 
24 extern "C" {
25 #include "ui_window.h"
26 #include "beos.h"
27 #include "../ui_screen.h"
28 #include "../ui_selection_encoding.h"
29 
30 #include <pobl/bl_str.h> /* strdup */
31 #include <pobl/bl_privilege.h>
32 #include <pobl/bl_unistd.h> /* usleep/bl_getuid/bl_getgid */
33 #include <pobl/bl_file.h> /* bl_file_set_cloexec */
34 #include <pobl/bl_debug.h>
35 #include <sys/select.h>
36 #include <mef/ef_utf16_parser.h>
37 }
38 
39 #define IS_UISCREEN(win) ((win)->xim_listener)
40 
41 class MLWindow : public BWindow {
42 public:
43   MLWindow(BRect frame, const char *title, window_type type, uint32 flags);
44   virtual void WindowActivated(bool active);
45   virtual void FrameResized(float width, float height);
46   virtual void Quit();
47   ui_window_t *uiwindow;
48 };
49 
50 class MLView : public BView {
51 private:
52   int32 buttons;
53   int32 dnd_modifiers;
54   const BFont *cur_font;
55 
56 public:
57   MLView(BRect frame, const char *name, uint32 resizemode, uint32 flags);
58   virtual void Draw(BRect updaterect);
59   virtual void KeyDown(const char *bytes, int32 numBytes);
60   virtual void MouseDown(BPoint where);
61   virtual void MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage);
62   virtual void MouseUp(BPoint where);
63   virtual void SetFont(const BFont *font, uint32 mask = B_FONT_ALL);
64   virtual void MessageReceived(BMessage *msg);
65   ui_window_t *uiwindow;
66   BMessenger *messenger;
67 };
68 
69 class ConnectDialog : public BWindow {
70 private:
71   int button;
72 
73 public:
74   ConnectDialog();
75   bool Go();
76   void Position(BWindow *parent);
77   virtual void MessageReceived(BMessage *message);
78   BTextControl *text;
79 };
80 
81 enum {
82   MSG_OK = 'ok',
83   MSG_CANCEL = 'can'
84 };
85 
86 static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
87 
88 /* --- static variables --- */
89 
90 static ef_parser_t *utf16_parser;
91 
92 /* --- static functions --- */
93 
set_input_focus(MLView * view)94 static void set_input_focus(MLView *view) {
95   view->MakeFocus(true);
96 
97   XEvent ev;
98 
99   ev.type = UI_KEY_FOCUS_IN;
100   ui_window_receive_event(view->uiwindow, &ev);
101 }
102 
update_ime_text(ui_window_t * uiwindow,const char * preedit_text)103 static void update_ime_text(ui_window_t *uiwindow, const char *preedit_text) {
104   (*uiwindow->preedit)(uiwindow, preedit_text, NULL);
105 }
106 
draw_screen(ui_window_t * uiwindow,BRect update,int force_expose)107 static void draw_screen(ui_window_t *uiwindow, BRect update, int force_expose) {
108   XExposeEvent ev;
109 
110   ev.type = UI_EXPOSE;
111   ev.x = update.left;
112   ev.y = update.top;
113   ev.width = update.right - update.left + 1.0;
114   ev.height = update.bottom - update.top + 1.0;
115   ev.force_expose = force_expose;
116 
117   ui_window_receive_event(uiwindow, (XEvent *)&ev);
118   uiwindow->update_window_flag = 0;
119 }
120 
window_lock(MLWindow * window)121 static void window_lock(MLWindow *window) {
122 #ifdef __DEBUG
123   bl_debug_printf("Locking %p(%s)\n", uiwindow, uiwindow->parent ? "child" : "root");
124 #endif
125 
126 #ifdef DEBUG
127   if (!window->IsLocked()) {
128     bl_debug_printf(BL_DEBUG_TAG " Looper is not locked\n");
129     beos_lock();
130   } else
131 #endif
132   {
133     window->UnlockLooper();
134     beos_lock();
135     window->LockLooper();
136   }
137 
138 #ifdef __DEBUG
139   bl_debug_printf("Done\n");
140 #endif
141 }
142 
view_lock(MLView * view)143 static void view_lock(MLView *view) {
144 #ifdef __DEBUG
145   bl_debug_printf("Locking %p(%s)\n", uiwindow, uiwindow->parent ? "child" : "root");
146 #endif
147 
148 #ifdef DEBUG
149   if (!view->Window()->IsLocked()) {
150     bl_debug_printf(BL_DEBUG_TAG " Looper is not locked\n");
151     beos_lock();
152   } else
153 #endif
154   {
155     view->UnlockLooper();
156     beos_lock();
157     view->LockLooper();
158   }
159 
160 #ifdef __DEBUG
161   bl_debug_printf("Done\n");
162 #endif
163 }
164 
165 /* --- class methods --- */
166 
167 /* Lock is unnecessary because no mlterm function is called. */
MLWindow(BRect frame,const char * title,window_type type,uint32 flags)168 MLWindow::MLWindow(BRect frame, const char *title, window_type type, uint32 flags)
169   : BWindow(frame, title, type, flags) {
170   frame.OffsetTo(0, 0);
171 }
172 
WindowActivated(bool active)173 void MLWindow::WindowActivated(bool active) {
174   if (!uiwindow) {
175     /* WindowActivated() can be called by BWindow::Quit() in window_dealloc() */
176     return;
177   }
178 
179   XEvent ev;
180 
181   if (active) {
182     ev.type = UI_FOCUS_IN;
183   } else {
184     ev.type = UI_FOCUS_OUT;
185   }
186 
187   window_lock(this);
188 
189   ui_window_receive_event(ui_get_root_window(uiwindow), &ev);
190 
191   beos_unlock();
192 }
193 
194 /*
195  * I don't know why but the value of 'width' or 'height' is 'right - left' or
196  * 'bottom - top'.
197  */
FrameResized(float width,float height)198 void MLWindow::FrameResized(float width, float height) {
199   window_lock(this);
200 
201   /* MLWindow::ResizeTo() causes FrameResized() event. */
202   if (uiwindow->width != ((u_int)width) + 1 - uiwindow->hmargin * 2 ||
203       uiwindow->height != ((u_int)height) + 1 - uiwindow->vmargin * 2) {
204     uiwindow->width = width + 1 - uiwindow->hmargin * 2;
205     uiwindow->height = height + 1 - uiwindow->vmargin * 2;
206 
207     XEvent ev;
208 
209     ev.type = UI_RESIZE;
210     ui_window_receive_event(uiwindow, &ev);
211   }
212 
213   beos_unlock();
214 }
215 
Quit()216 void MLWindow::Quit() {
217   if (uiwindow) {
218     if (uiwindow->disp->num_roots == 1) {
219       be_app->PostMessage(B_QUIT_REQUESTED);
220     }
221 
222     XEvent ev;
223 
224     ev.type = UI_CLOSE_WINDOW;
225 
226     window_lock(this);
227 
228     ui_window_receive_event(uiwindow, &ev);
229 
230     /*
231      * Not reach.
232      * window_destroyed() => ui_window_final() => window_dealloc() =>
233      * MLWindow::Quit() => BWindow::Quit()
234      */
235   } else {
236     /* Called from window_dealloc() */
237 
238     beos_unlock();
239 
240     BWindow::Quit();
241 
242     /* Not reach. This thread exits in BWindow::Quit(). */
243   }
244 }
245 
246 /* Lock is unnecessary because no mlterm function is called. */
MLView(BRect frame,const char * title,uint32 resizemode,uint32 flags)247 MLView::MLView(BRect frame, const char *title, uint32 resizemode, uint32 flags)
248   : BView(frame, title, resizemode, flags) {
249   SetViewColor(255, 255, 255);
250   SetHighColor(0, 0, 0);
251   SetLowColor(0, 0, 0);
252   SetFontSize(14);
253   SetDrawingMode(B_OP_OVER);
254   cur_font = NULL;
255   buttons = 0;
256   messenger = NULL;
257 
258   /* uiwindow is initialized in view_alloc() or window_alloc(). */
259 #if 0
260   uiwindow = NULL;
261 #endif
262 }
263 
Draw(BRect update)264 void MLView::Draw(BRect update) {
265   if (!uiwindow || (IS_UISCREEN(uiwindow) && !((ui_screen_t *)uiwindow)->term)) {
266     /* It has been already removed from ui_layout or term has been detached. */
267     return;
268   }
269 
270   view_lock(this);
271 
272   draw_screen(uiwindow, update, 0);
273 
274   beos_unlock();
275 }
276 
KeyDown(const char * bytes,int32 numBytes)277 void MLView::KeyDown(const char *bytes, int32 numBytes) {
278   if (!uiwindow || (IS_UISCREEN(uiwindow) && !((ui_screen_t *)uiwindow)->term)) {
279     /* It has been already removed from ui_layout or term has been detached. */
280     return;
281   }
282 
283   view_lock(this);
284 
285   int32 modifiers;
286 
287   Window()->CurrentMessage()->FindInt32((const char*)"modifiers", &modifiers);
288 
289   XKeyEvent kev;
290 
291   kev.type = UI_KEY_PRESS;
292   kev.state = modifiers & (ShiftMask|ControlMask|Mod1Mask|CommandMask);
293 
294   if (numBytes == 1) {
295     if (bytes[0] == B_FUNCTION_KEY) {
296       int32 key;
297 
298       Window()->CurrentMessage()->FindInt32((const char*)"key", &key);
299       kev.keysym = (key | 0xf000);
300       kev.utf8 = NULL;
301     } else {
302       kev.keysym = *bytes;
303 
304       switch(kev.keysym) {
305       case B_LEFT_ARROW:  /* 0x61 (raw key - jp106) */
306       case B_RIGHT_ARROW: /* 0x63 */
307       case B_UP_ARROW:    /* 0x57 */
308       case B_DOWN_ARROW:  /* 0x62 */
309       case B_INSERT:      /* 0x1f */
310       case B_DELETE:      /* 0x34 */
311       case B_HOME:        /* 0x20 */
312       case B_END:         /* 0x35 */
313       case B_PAGE_UP:     /* 0x21 */
314       case B_PAGE_DOWN:   /* 0x36 */
315         if (kev.state & ControlMask) {
316           int32 key;
317 
318           Window()->CurrentMessage()->FindInt32((const char*)"key", &key); /* raw key */
319           if (key <= 0x1e || (0x22 <= key && key <= 0x33) || (0x37 <= key && key <= 0x56) ||
320               (0x58 <= key && key <= 0x60) || 0x64 <= key) {
321             kev.utf8 = bytes;
322             break;
323           }
324         }
325 
326         kev.keysym |= 0xe000;
327         kev.utf8 = NULL;
328         break;
329 
330       case B_KATAKANA_HIRAGANA:
331       case B_HANKAKU_ZENKAKU:
332       case B_HANGUL:
333       case B_HANGUL_HANJA:
334         kev.utf8 = NULL;
335         break;
336 
337       default:
338         kev.utf8 = bytes;
339         break;
340       }
341     }
342   } else {
343     kev.keysym = 0;
344     kev.utf8 = bytes;
345   }
346 
347   ui_window_receive_event(uiwindow, (XEvent *)&kev);
348 
349   beos_unlock();
350 }
351 
MouseDown(BPoint where)352 void MLView::MouseDown(BPoint where) {
353   if (!uiwindow || (IS_UISCREEN(uiwindow) && !((ui_screen_t *)uiwindow)->term)) {
354     /* It has been already removed from ui_layout or term has been detached. */
355     return;
356   }
357 
358   view_lock(this);
359 
360   if (!IsFocus()) {
361     set_input_focus(this);
362   }
363 
364   int32 clicks;
365   int64 when; /* microsec */
366   int32 modifiers;
367 
368   Window()->CurrentMessage()->FindInt32((const char*)"buttons", &this->buttons);
369   Window()->CurrentMessage()->FindInt32((const char*)"clicks", &clicks);
370   Window()->CurrentMessage()->FindInt64((const char*)"when", &when);
371   Window()->CurrentMessage()->FindInt32((const char*)"modifiers", &modifiers);
372 
373   XButtonEvent bev;
374 
375   bev.type = UI_BUTTON_PRESS;
376   bev.time = when / 1000; /* msec */
377   bev.x = where.x;
378   bev.y = where.y;
379   bev.state = modifiers & (ShiftMask|ControlMask|Mod1Mask|CommandMask);
380   bev.click_count = clicks;
381   if (buttons == 0) {
382     bev.button = 0;
383   } else {
384     if (buttons & B_PRIMARY_MOUSE_BUTTON) {
385       bev.button = 1;
386     } else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
387       bev.button = 3;
388     } else /* if (buttons & B_TERTIARY_MOUSE_BUTTON) */ {
389       bev.button = 2;
390     }
391   }
392 
393   ui_window_receive_event(uiwindow, (XEvent *)&bev);
394 
395   beos_unlock();
396 }
397 
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)398 void MLView::MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage) {
399   if (dragMessage) {
400     Window()->CurrentMessage()->FindInt32((const char*)"modifiers", &dnd_modifiers);
401 
402     return;
403   }
404 
405   if (!uiwindow || (IS_UISCREEN(uiwindow) && !((ui_screen_t *)uiwindow)->term)) {
406     /* It has been already removed from ui_layout or term has been detached. */
407     return;
408   }
409 
410   view_lock(this);
411 
412   int32 buttons;
413   int64 when; /* microsec */
414   int32 modifiers;
415 
416   Window()->CurrentMessage()->FindInt32((const char*)"buttons", &buttons);
417   Window()->CurrentMessage()->FindInt64((const char*)"when", &when);
418   Window()->CurrentMessage()->FindInt32((const char*)"modifiers", &modifiers);
419 
420   XMotionEvent mev;
421 
422   mev.time = when / 1000; /* msec */
423   mev.x = where.x;
424   mev.y = where.y;
425   mev.state = modifiers & (ShiftMask|ControlMask|Mod1Mask|CommandMask);
426   if (buttons == 0) {
427     mev.type = UI_POINTER_MOTION;
428   } else {
429     if (buttons & B_PRIMARY_MOUSE_BUTTON) {
430       mev.state |= Button1Mask;
431     }
432     if (buttons & B_SECONDARY_MOUSE_BUTTON) {
433       mev.state |= Button3Mask;
434     }
435     if (buttons & B_TERTIARY_MOUSE_BUTTON) {
436       mev.state |= Button2Mask;
437     }
438     mev.type = UI_BUTTON_MOTION;
439   }
440 
441   ui_window_receive_event(uiwindow, (XEvent *)&mev);
442 
443   beos_unlock();
444 }
445 
MouseUp(BPoint where)446 void MLView::MouseUp(BPoint where) {
447   if (!uiwindow || (IS_UISCREEN(uiwindow) && !((ui_screen_t *)uiwindow)->term)) {
448     /* It has been already removed from ui_layout or term has been detached. */
449     return;
450   }
451 
452   view_lock(this);
453 
454   int32 clicks;
455   int64 when; /* microsec */
456 
457   Window()->CurrentMessage()->FindInt32((const char*)"clicks", &clicks);
458   Window()->CurrentMessage()->FindInt64((const char*)"when", &when);
459 
460   XButtonEvent bev;
461 
462   bev.type = UI_BUTTON_RELEASE;
463   bev.time = when / 1000; /* msec */
464   bev.x = where.x;
465   bev.y = where.y;
466   bev.state = 0;
467   if (buttons == 0) {
468     bev.button = 0;
469   } else {
470     if (buttons & B_PRIMARY_MOUSE_BUTTON) {
471       bev.button = 1;
472     } else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
473       bev.button = 3;
474     } else /* if (buttons & B_TERTIARY_MOUSE_BUTTON) */ {
475       bev.button = 2;
476     }
477   }
478   this->buttons = 0;
479 
480   ui_window_receive_event(uiwindow, (XEvent *)&bev);
481 
482   beos_unlock();
483 }
484 
485 /*
486  * SetFont() doesn't check if the specified font is different from the current font,
487  * while SetHighColor() checks if the specified RGB is different from the current RGB.
488  * (https://github.com/haiku/haiku/blob/master/src/kits/interface/View.cpp)
489  */
SetFont(const BFont * font,uint32 mask)490 void MLView::SetFont(const BFont *font, uint32 mask) {
491   if (cur_font != font) {
492     cur_font = font;
493     BView::SetFont(font, mask);
494   }
495 }
496 
MessageReceived(BMessage * message)497 void MLView::MessageReceived(BMessage *message) {
498   if (message->what == B_SIMPLE_DATA) {
499     int32 count = 0;
500 
501 #if 0
502     status_t s;
503     int32 action;
504 
505     while ((s = message->FindInt32("be:actions", count++, &action)) == B_OK) {
506       bl_debug_printf(BL_DEBUG_TAG "ACTION: %x (COYP %x MOVE %x)\n",
507                       action, B_COPY_TARGET, B_MOVE_TARGET);
508     }
509 #ifdef DEBUG
510     if (s == B_NAME_NOT_FOUND) {
511       bl_debug_printf(BL_DEBUG_TAG " be:actions is not found.\n");
512     }
513 #endif
514 
515     count = 0;
516 #endif
517 
518     entry_ref ref;
519     while (message->FindRef("refs", count++, &ref) == B_OK) {
520       BPath path(&ref);
521 
522       view_lock(this);
523 
524       if (dnd_modifiers & ShiftMask) {
525         (*uiwindow->set_xdnd_config)(uiwindow, NULL, "scp", (char*)path.Path());
526       } else {
527         XSelectionNotifyEvent ev;
528         ev.type = UI_SELECTION_NOTIFIED;
529         ev.data = (char*)path.Path();
530         ev.len = strlen(ev.data);
531 
532         ui_window_receive_event(uiwindow, (XEvent*)&ev);
533       }
534 
535       beos_unlock();
536     }
537   } else if (message->what == B_INPUT_METHOD_EVENT) {
538     if (IS_UISCREEN(uiwindow) && !((ui_screen_t *)uiwindow)->term) {
539       /* It has been already removed from ui_layout or term has been detached. */
540       return;
541     }
542 
543     view_lock(this);
544 
545     int32 opcode;
546 
547     /*
548      * If a focused view is changed from a view where you are inputting with
549      * ime to another in splitted screen, following check enclosed by
550      * #if 1 ... #endif is necessary.
551      */
552     if (message->FindInt32("be:opcode", &opcode) == B_OK) {
553       if (opcode == B_INPUT_METHOD_STARTED) {
554         BMessenger m;
555 
556         if (message->FindMessenger("be:reply_to", &m) == B_OK) {
557           if (messenger) {
558             delete messenger;
559           }
560 
561           messenger = new BMessenger(m);
562         }
563       } else if (opcode == B_INPUT_METHOD_STOPPED) {
564         if (messenger) {
565           delete messenger;
566           messenger = NULL;
567         }
568       } else if (opcode == B_INPUT_METHOD_CHANGED) {
569         const char *str;
570 
571         if (message->FindString("be:string", &str) != B_OK || *str == '\0') {
572           update_ime_text(uiwindow, "");
573 
574           goto end;
575         }
576 
577         bool confirmed;
578 
579         if (message->FindBool("be:confirmed", &confirmed) == B_OK && confirmed) {
580           update_ime_text(uiwindow, "");
581 
582           XKeyEvent kev;
583 
584           kev.type = UI_KEY_PRESS;
585           kev.state = 0;
586           kev.keysym = 0;
587           kev.utf8 = str;
588 
589           ui_window_receive_event(uiwindow, (XEvent *)&kev);
590         } else {
591           int32 start;
592           int32 end;
593 #if 0
594           int32 count = 0;
595 
596           while (message->FindInt32("be:clause_start", count, &start) == B_OK &&
597                  message->FindInt32("be:clause_end", count, &end) == B_OK) {
598             bl_debug_printf("Clause %d: %d-%d\n", count, start, end);
599             count++;
600           }
601 #endif
602 
603           message->FindInt32("be:selection", 1, &end);
604           if (end > 0) {
605             message->FindInt32("be:selection", 0, &start);
606 
607             char str2[strlen(str) + 10];
608             char *p = str2;
609 
610             memcpy(p, str, start);
611             strcpy((p += start), "\x1b[7m");
612 
613             int32 len = end - start;
614             strncpy((p += 4), str + start, len);
615             strcpy((p += len), "\x1b[27m");
616             strcpy((p += 5), str + end);
617 
618             update_ime_text(uiwindow, str2);
619           } else {
620             update_ime_text(uiwindow, str);
621           }
622         }
623       } else if (opcode == B_INPUT_METHOD_LOCATION_REQUEST && messenger) {
624         int x;
625         int y;
626 
627         if (!uiwindow->xim_listener ||
628             !(*uiwindow->xim_listener->get_spot)(uiwindow->xim_listener->self, &x, &y)) {
629           x = y = 0;
630         }
631 
632         BMessage message(B_INPUT_METHOD_EVENT);
633         message.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST);
634         BPoint where((float)x, (float)y);
635         ConvertToScreen(&where);
636 
637         float height = ui_line_height((ui_screen_t*)uiwindow);
638         message.AddPoint("be:location_reply", where);
639         message.AddFloat("be:height_reply", height);
640         messenger->SendMessage(&message);
641       }
642     }
643 
644   end:
645     beos_unlock();
646   } else if (message->what == B_MOUSE_WHEEL_CHANGED) {
647     int64 when; /* microsec */
648     float y;
649     XButtonEvent bev;
650 
651     message->FindFloat("be:wheel_delta_y", &y);
652     message->FindInt64("be:when", &when);
653 
654     bev.type = UI_BUTTON_PRESS;
655     bev.time = when / 1000; /* msec */
656     bev.x = 0;
657     bev.y = 0;
658     bev.state = 0;
659     bev.click_count = 1;
660     if (y < 0) {
661       bev.button = 4;
662     } else {
663       bev.button = 5;
664     }
665 
666     view_lock(this);
667     ui_window_receive_event(uiwindow, (XEvent *)&bev);
668     beos_unlock();
669   } else {
670     BView::MessageReceived(message);
671   }
672 }
673 
674 /* --- global functions --- */
675 
view_alloc(ui_window_t * uiwindow,int x,int y,u_int width,u_int height)676 void view_alloc(ui_window_t *uiwindow, int x, int y, u_int width, u_int height) {
677   uint32 flags = B_WILL_DRAW;
678 
679   if (uiwindow->key_pressed) {
680     flags |= B_INPUT_METHOD_AWARE;
681   }
682 
683   MLView *view = new MLView(BRect(x, y, x + width - 1, y + height - 1), "mlterm",
684                             B_FOLLOW_NONE, flags);
685 
686   MLWindow *win = (MLWindow*)uiwindow->parent->my_window;
687   if (win->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
688     delete view;
689 
690     return;
691   }
692 
693   view->uiwindow = uiwindow;
694   win->AddChild(view);
695   uiwindow->my_window = (void*)view;
696 
697   if (uiwindow->inputtable) {
698     view->MakeFocus(true);
699   }
700 
701   win->UnlockLooper();
702 }
703 
view_dealloc(void * view)704 void view_dealloc(/* BView */ void *view) {
705   BWindow *win = ((BView*)view)->Window();
706 
707   if (win->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
708     return;
709   }
710 
711   win->RemoveChild((BView*)view);
712   ((MLView*)view)->uiwindow = None;
713   delete (BView*)view;
714 
715   win->UnlockLooper();
716 }
717 
view_update(void * view,int force_expose)718 void view_update(/* BView */ void *view, int force_expose) {
719   ui_window_t *uiwindow = ((MLView*)view)->uiwindow;
720   int x;
721   int y;
722 
723   if (!uiwindow->xim_listener ||
724       !(*uiwindow->xim_listener->get_spot)(uiwindow->xim_listener->self, &x, &y)) {
725     x = y = 0;
726   }
727 
728   x += (uiwindow->hmargin);
729   y += (uiwindow->vmargin);
730 
731   draw_screen(uiwindow, BRect(x, y, x, y), force_expose);
732 }
733 
view_set_clip(void * view,int x,int y,u_int width,u_int height)734 void view_set_clip(/* BView */ void *view, int x, int y, u_int width, u_int height) {
735   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
736     return;
737   }
738 
739   BRegion *region = new BRegion(BRect(x, y, x + width - 1, y + height - 1));
740   ((BView*)view)->ConstrainClippingRegion(region);
741 
742   ((BView*)view)->UnlockLooper();
743 }
744 
view_unset_clip(void * view)745 void view_unset_clip(/* BView */ void *view) {
746   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
747     return;
748   }
749 
750   ((BView*)view)->ConstrainClippingRegion(NULL);
751 
752   ((BView*)view)->UnlockLooper();
753 }
754 
view_draw_string(void * view,ui_font_t * font,ui_color_t * fg_color,int x,int y,char * str,size_t len)755 void view_draw_string(/* BView */ void *view, ui_font_t *font, ui_color_t *fg_color,
756                       int x, int y, char *str, size_t len) {
757   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
758     return;
759   }
760 
761   u_int32_t pixel = fg_color->pixel;
762   ((BView*)view)->SetHighColor((pixel >> 16) & 0xff, (pixel >> 8) & 0xff, pixel & 0xff,
763                                (pixel >> 24) & 0xff);
764   ((BView*)view)->SetFont((BFont*)font->xfont->fid);
765 
766   x += font->x_off;
767 
768   /*
769    * https://www.haiku-os.org/legacy-docs/bebook/BView.html
770    * The BeOS draws text one pixel above the logical baseline to maintain
771    * compatibility with an earlier version of one of our most commonly-used font
772    * rasterizers. This affects both fonts and BShapes representing glyphs
773    * (see BFont::GetGlyphShapes(). To draw text at the right place, add one to
774    * the Y coordinate when calling MovePenTo() or specifying a BPoint at which
775    * to begin drawing.
776    */
777   ((BView*)view)->MovePenTo(x, y + 1);
778   ((BView*)view)->DrawString(str, len);
779 
780   if (font->double_draw_gap) {
781     ((BView*)view)->MovePenTo(x + font->double_draw_gap, y + 1);
782     ((BView*)view)->DrawString(str, len);
783   }
784 
785   ((BView*)view)->UnlockLooper();
786 }
787 
view_draw_string16(void * view,ui_font_t * font,ui_color_t * fg_color,int x,int y,XChar2b * str,size_t len)788 void view_draw_string16(/* BView */ void *view, ui_font_t *font, ui_color_t *fg_color,
789                         int x, int y, XChar2b *str, size_t len) {
790   if (!utf16_parser) {
791     utf16_parser = ef_utf16le_parser_new(); /* XXX leaked */
792   }
793   ef_conv_t *utf8_conv = ui_get_selection_conv(1);
794 
795   char str2[len * UTF_MAX_SIZE];
796 
797   (*utf16_parser->init)(utf16_parser);
798   (*utf16_parser->set_str)(utf16_parser, (u_char*)str, len * 2);
799   (*utf8_conv->init)(utf8_conv);
800   len = (*utf8_conv->convert)(utf8_conv, (u_char*)str2, len * UTF_MAX_SIZE, utf16_parser);
801 
802   /* LockLooper() is called in view_draw_string() */
803   view_draw_string(view, font, fg_color, x, y, str2, len);
804 }
805 
view_fill_with(void * view,ui_color_t * color,int x,int y,u_int width,u_int height)806 void view_fill_with(/* BView */ void *view, ui_color_t *color, int x, int y,
807                     u_int width /* > 0 */, u_int height /* > 0 */) {
808   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
809     return;
810   }
811 
812   u_long pixel = color->pixel;
813   ((BView*)view)->SetLowColor((pixel >> 16) & 0xff, (pixel >> 8) & 0xff, pixel & 0xff,
814                               (pixel >> 24) & 0xff);
815   ((BView*)view)->FillRect(BRect(x, y, x + width - 1, y + height - 1), B_SOLID_LOW);
816 
817   ((BView*)view)->UnlockLooper();
818 }
819 
view_draw_rect_frame(void * view,ui_color_t * color,int x1,int y1,int x2,int y2)820 void view_draw_rect_frame(/* BView */ void *view, ui_color_t *color, int x1, int y1,
821                           int x2, int y2) {
822   u_long pixel = color->pixel;
823   rgb_color rgb;
824 
825   rgb.red = (pixel >> 16) & 0xff;
826   rgb.green = (pixel >> 8) & 0xff;
827   rgb.blue = pixel & 0xff;
828   rgb.alpha = (pixel >> 24) & 0xff;
829 
830   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
831     return;
832   }
833 
834   ((BView*)view)->BeginLineArray(4);
835   ((BView*)view)->AddLine(BPoint(x1, y1), BPoint(x1, y2), rgb);
836   ((BView*)view)->AddLine(BPoint(x1, y2), BPoint(x2, y2), rgb);
837   ((BView*)view)->AddLine(BPoint(x2, y1), BPoint(x2, y2), rgb);
838   ((BView*)view)->AddLine(BPoint(x1, y1), BPoint(x2, y1), rgb);
839   ((BView*)view)->EndLineArray();
840 
841   ((BView*)view)->UnlockLooper();
842 }
843 
view_copy_area(void * view,Pixmap src,int src_x,int src_y,u_int width,u_int height,int dst_x,int dst_y)844 void view_copy_area(/* BView */ void *view, Pixmap src, int src_x, int src_y,
845                     u_int width, u_int height, int dst_x, int dst_y) {
846   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
847     return;
848   }
849 
850   ((BView*)view)->DrawBitmap((BBitmap*)src,
851                              BRect(src_x, src_y, src_x + width - 1, src_y + height - 1),
852                              BRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1));
853 
854   ((BView*)view)->UnlockLooper();
855 }
856 
view_scroll(void * view,int src_x,int src_y,u_int width,u_int height,int dst_x,int dst_y)857 void view_scroll(/* BView */ void *view, int src_x, int src_y, u_int width,
858                  u_int height, int dst_x, int dst_y) {
859 #if 0
860   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
861     return;
862   }
863 
864   ((BView*)view)->CopyBits(BRect(src_x, src_y, src_x + width - 1, src_y + height - 1),
865                            BRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1));
866 
867   ((BView*)view)->UnlockLooper();
868 #endif
869 }
870 
view_bg_color_changed(void * view)871 void view_bg_color_changed(/* BView */ void *view) {
872 }
873 
view_visual_bell(void * view)874 void view_visual_bell(/* BView */ void *view) {
875   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
876     return;
877   }
878 
879   u_long pixel = ((MLView*)view)->uiwindow->fg_color.pixel;
880   ((BView*)view)->SetLowColor((pixel >> 16) & 0xff, (pixel >> 8) & 0xff, pixel & 0xff,
881                               (pixel >> 24) & 0xff);
882 
883   ui_window_t *win = ((MLView*)view)->uiwindow;
884   ((BView*)view)->FillRect(BRect(0, 0, ACTUAL_WIDTH(win) - 1, ACTUAL_HEIGHT(win) - 1), B_SOLID_LOW);
885 
886   ((BView*)view)->UnlockLooper();
887 
888   usleep(100000); /* 100 msec */
889 
890   draw_screen(((MLView*)view)->uiwindow,
891               BRect(0, 0, ACTUAL_WIDTH(win) - 1, ACTUAL_HEIGHT(win) - 1), 1);
892 }
893 
view_set_input_focus(void * view)894 void view_set_input_focus(/* BView */ void *view) {
895   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
896     return;
897   }
898 
899   set_input_focus((MLView*)view);
900 
901   ((BView*)view)->UnlockLooper();
902 }
903 
view_resize(void * view,u_int width,u_int height)904 void view_resize(/* BView */ void *view, u_int width, u_int height) {
905   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
906     return;
907   }
908 
909   ((BView*)view)->ResizeTo((float)width - 1.0, (float)height - 1.0);
910 
911   ((BView*)view)->UnlockLooper();
912 }
913 
view_move(void * view,int x,int y)914 void view_move(/* BView */ void *view, int x, int y) {
915   if (((BView*)view)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
916     return;
917   }
918 
919   ((BView*)view)->MoveTo((float)x, (float)y);
920 
921   ((BView*)view)->UnlockLooper();
922 }
923 
view_set_hidden(void * view,int flag)924 void view_set_hidden(/* BView */ void *view, int flag) {
925 }
926 
view_reset_uiwindow(ui_window_t * uiwindow)927 void view_reset_uiwindow(ui_window_t *uiwindow) {
928   ((MLView*)uiwindow->my_window)->uiwindow = uiwindow;
929 }
930 
window_alloc(ui_window_t * root,int x,int y,u_int width,u_int height,int popup,int geom_hint)931 void window_alloc(ui_window_t *root, int x, int y, u_int width, u_int height, int popup,
932                   int geom_hint) {
933   MLWindow *window;
934 
935   if (popup) {
936     window = new MLWindow(BRect(x, y, x + width - 1, y + height - 1), "",
937                           B_BORDERED_WINDOW,
938                           B_NOT_RESIZABLE|B_NOT_CLOSABLE|B_NOT_ZOOMABLE|B_NOT_MINIMIZABLE|
939                           B_NOT_MOVABLE|B_AVOID_FOCUS|B_NOT_ANCHORED_ON_ACTIVATE);
940 
941     MLView *view = new MLView(BRect(0, 0, width - 1, height - 1), "IM", B_FOLLOW_NONE, B_WILL_DRAW);
942 
943     view->uiwindow = root;
944     window->AddChild(view);
945   } else {
946     /* XXX */
947     static int x_count = 0;
948     static int y_count = 0;
949     BRect r = BScreen().Frame();
950 
951     if ((geom_hint & XValue) == 0) {
952       x = ((r.right - width) / 8) * x_count + 50;
953       if (++x_count == 8) {
954         x_count = 0;
955       }
956     }
957     if ((geom_hint & YValue) == 0) {
958       y = ((r.bottom - height) / 8) * y_count + 50;
959       if (++y_count == 8) {
960         y_count = 0;
961       }
962     }
963 
964     window = new MLWindow(BRect(x, y, x + width - 1, y + height - 1),
965                           "mlterm", B_DOCUMENT_WINDOW, 0);
966     if (root->disp->width == 0) {
967       BScreen *screen = new BScreen(window);
968       BRect rect = screen->Frame();
969       root->disp->width = rect.right + 1;
970       root->disp->height = rect.bottom + 1;
971       delete screen;
972     }
973   }
974 
975   window->uiwindow = root;
976   root->my_window = (void*)window;
977 }
978 
window_show(void * window)979 void window_show(void *window) {
980   if (((BWindow*)window)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
981     return;
982   }
983 
984   ((BWindow*)window)->Show();
985 
986   ((BWindow*)window)->UnlockLooper();
987 }
988 
window_dealloc(void * window)989 void window_dealloc(/* BWindow */ void *window) {
990   if (((BWindow*)window)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
991     return;
992   }
993 
994   ((MLWindow*)window)->uiwindow = None;
995 
996   ((BWindow*)window)->Quit();
997   /* Not reach. This thread exits in BWindow::Quit(). */
998 }
999 
window_move(void * window,int x,int y)1000 void window_move(/* BWindow */ void *window, int x, int y) {
1001   if (((BWindow*)window)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
1002     return;
1003   }
1004 
1005   ((BWindow*)window)->MoveTo((float)x, (float)y);
1006 
1007   ((BWindow*)window)->UnlockLooper();
1008 }
1009 
window_resize(void * window,int width,int height)1010 void window_resize(/* BWindow */ void *window, int width, int height) {
1011   if (((BWindow*)window)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
1012     return;
1013   }
1014 
1015   ((BWindow*)window)->ResizeTo((float)width - 1.0, (float)height - 1.0);
1016 
1017   BView *view;
1018   if ((view = (BView*)window_get_orphan(window, 0))) {
1019     ((BView*)view)->ResizeTo((float)width - 1.0, (float)height - 1.0);
1020   }
1021 
1022   ((BWindow*)window)->UnlockLooper();
1023 }
1024 
window_get_position(void * window,int * x,int * y)1025 void window_get_position(/* BWindow */ void *window, int *x, int *y) {
1026   BRect rect = ((BWindow*)window)->Frame();
1027   *x = rect.left;
1028   *y = rect.top;
1029 }
1030 
window_set_title(void * window,const char * title)1031 void window_set_title(/* BWindow */ void *window, const char *title /* utf8 */) {
1032   if (((BWindow*)window)->LockLooperWithTimeout(B_INFINITE_TIMEOUT) != B_OK) {
1033     return;
1034   }
1035 
1036   ((BWindow*)window)->SetTitle(title);
1037 
1038   ((BWindow*)window)->UnlockLooper();
1039 }
1040 
window_get_orphan(void * window,int idx)1041 void *window_get_orphan(void *window, int idx) {
1042   if (((MLWindow*)window)->uiwindow->num_children == 0) {
1043     void *child = ((BWindow*)window)->ChildAt(idx);
1044 
1045     return child;
1046   } else {
1047     return NULL;
1048   }
1049 }
1050 
app_urgent_bell(int on)1051 void app_urgent_bell(int on) {
1052 }
1053 
beos_create_font(const char * font_family,float size,int is_italic,int is_bold)1054 void *beos_create_font(const char *font_family, float size, int is_italic, int is_bold) {
1055   BFont *bfont = new BFont();
1056 
1057   bfont->SetFamilyAndStyle(font_family, is_bold ? "Bold" : "Regular");
1058   bfont->SetSpacing(B_FIXED_SPACING);
1059   bfont->SetSize(size);
1060   if (is_italic) {
1061     bfont->SetShear(100.0);
1062   }
1063 
1064   return bfont;
1065 }
1066 
1067 #ifdef USE_OT_LAYOUT
beos_get_font_family(void * bfont)1068 char *beos_get_font_family(void *bfont) {
1069   font_family family;
1070 
1071   ((BFont*)bfont)->GetFamilyAndStyle(&family, NULL);
1072 
1073   return strdup(family);
1074 }
1075 #endif
1076 
beos_release_font(void * bfont)1077 void beos_release_font(void *bfont) {
1078   delete (BFont*)bfont;
1079 }
1080 
beos_font_get_metrics(void * bfont,u_int * width,u_int * height,u_int * ascent)1081 void beos_font_get_metrics(void *bfont, u_int *width, u_int *height, u_int *ascent) {
1082   const char *strs[] = { "M" };
1083   int32 lens[] = { 1 };
1084   float widths[1];
1085 
1086   ((BFont*)bfont)->GetStringWidths(strs, lens, 1, widths);
1087   *width = widths[0] + 0.5;
1088 
1089   font_height height_info;
1090   ((BFont*)bfont)->GetHeight(&height_info);
1091   *height = (height_info.ascent + height_info.descent + height_info.leading) + 0.5;
1092   *ascent = (height_info.ascent + 0.5);
1093 }
1094 
beos_font_get_advance(void * bfont,int size_attr,unichar * utf16,u_int len,u_int32_t glyph)1095 u_int beos_font_get_advance(void *bfont, int size_attr,
1096                             unichar *utf16, u_int len, u_int32_t glyph) {
1097   if (!utf16_parser) {
1098     /* XXX leaked */
1099     utf16_parser = ef_utf16le_parser_new();
1100   }
1101   ef_conv_t *utf8_conv = ui_get_selection_conv(1);
1102 
1103   char utf8[len * UTF_MAX_SIZE];
1104 
1105   (*utf16_parser->init)(utf16_parser);
1106   (*utf16_parser->set_str)(utf16_parser, (u_char*)utf16, len * 2);
1107   (*utf8_conv->init)(utf8_conv);
1108   len = (*utf8_conv->convert)(utf8_conv, (u_char*)utf8, len * UTF_MAX_SIZE, utf16_parser);
1109 
1110   const char *strs[] = { utf8 };
1111   int32 lens[] = { len };
1112   float widths[1];
1113 
1114   ((BFont*)bfont)->GetStringWidths(strs, lens, 1, widths);
1115 
1116   return (u_int)(widths[0] + 0.5);
1117 }
1118 
beos_clipboard_set(const u_char * utf8,size_t len)1119 void beos_clipboard_set(const u_char *utf8, size_t len) {
1120   if (be_clipboard->Lock()) {
1121     be_clipboard->Clear();
1122 
1123     BMessage *clip = be_clipboard->Data();
1124     clip->AddData("text/plain", B_MIME_TYPE, utf8, len);
1125     be_clipboard->Commit();
1126     be_clipboard->Unlock();
1127   }
1128 }
1129 
beos_clipboard_get(u_char ** str,size_t * len)1130 int beos_clipboard_get(u_char **str, size_t *len) {
1131   if (be_clipboard->Lock()) {
1132     BMessage *clip = be_clipboard->Data();
1133 
1134     clip->FindData("text/plain", B_MIME_TYPE, (const void **)str, (ssize_t*)len);
1135     be_clipboard->Unlock();
1136 
1137     return 1;
1138   } else {
1139     return 0;
1140   }
1141 }
1142 
beos_beep(void)1143 void beos_beep(void) {
1144   beep();
1145 }
1146 
beos_create_image(const void * data,u_int len,u_int width,u_int height)1147 void *beos_create_image(const void *data, u_int len, u_int width, u_int height) {
1148   BBitmap *bitmap;
1149 
1150   if ((bitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGBA32))) {
1151     bitmap->SetBits(data, len, 0, B_RGBA32);
1152   }
1153 
1154   return bitmap;
1155 }
1156 
beos_destroy_image(void * bitmap)1157 void beos_destroy_image(void *bitmap) {
1158   delete (BBitmap*)bitmap;
1159 }
1160 
beos_load_image(const char * path,u_int * width,u_int * height)1161 void *beos_load_image(const char *path, u_int *width, u_int *height) {
1162   BBitmap *bitmap;
1163 
1164   if ((bitmap = BTranslationUtils::GetBitmap(path))) {
1165     BRect rect = bitmap->Bounds();
1166     *width = rect.right + 1;
1167     *height = rect.bottom + 1;
1168   }
1169 
1170   return bitmap;
1171 }
1172 
beos_resize_image(void * bitmap,u_int width,u_int height)1173 void *beos_resize_image(void *bitmap, u_int width, u_int height) {
1174   BRect rect(0, 0, width - 1, height - 1);
1175   BBitmap *new_bitmap = new BBitmap(rect, B_RGBA32, true);
1176   BView *view = new BView(rect, "", B_FOLLOW_ALL_SIDES, 0);
1177   new_bitmap->AddChild(view);
1178   new_bitmap->Lock();
1179   view->DrawBitmap((BBitmap*)bitmap, ((BBitmap*)bitmap)->Bounds(), rect);
1180   new_bitmap->Unlock();
1181 
1182   new_bitmap->RemoveChild(view);
1183   delete view;
1184 
1185   delete (BBitmap*)bitmap;
1186 
1187   return new_bitmap;
1188 }
1189 
beos_get_bits(void * bitmap)1190 u_char *beos_get_bits(void *bitmap) {
1191   return (u_char*)((BBitmap*)bitmap)->Bits();
1192 }
1193 
ConnectDialog()1194 ConnectDialog::ConnectDialog()
1195   : BWindow(BRect(0, 0, 200, 100), "Password", B_MODAL_WINDOW,
1196             B_NOT_RESIZABLE|B_NOT_CLOSABLE|B_NOT_ZOOMABLE|B_NOT_MINIMIZABLE|B_NOT_MOVABLE, 0) {
1197   BButton *ok = new BButton(BRect(0, 0, 100, 100), "OkButton", "OK", new BMessage(MSG_OK));
1198   BButton *cancel = new BButton(BRect(0, 0, 100, 100), "CancelButton", "Cancel",
1199                                 new BMessage(MSG_CANCEL));
1200 
1201   text = new BTextControl("", "Password", "", NULL);
1202   text->TextView()->HideTyping(true);
1203 
1204   BLayoutBuilder::Group<>(this, B_VERTICAL)
1205     .Add(text)
1206     .AddGroup(B_HORIZONTAL)
1207       .AddGlue()
1208       .Add(ok)
1209       .Add(cancel)
1210     .End()
1211     .SetInsets(B_USE_DEFAULT_SPACING);
1212 
1213   SetDefaultButton(ok);
1214   button = -1;
1215   text->MakeFocus();
1216 }
1217 
Go()1218 bool ConnectDialog::Go() {
1219   Show();
1220 
1221   while (button == -1) {
1222     usleep(1000);
1223   }
1224 
1225   return (button == 0);
1226 }
1227 
Position(BWindow * parent)1228 void ConnectDialog::Position(BWindow *parent) {
1229   BRect rect = (parent ? parent->Frame() : BScreen(this).Frame());
1230 
1231   MoveTo((rect.right + rect.left) / 2 - 100, (rect.bottom + rect.top) / 2 - 50);
1232 }
1233 
MessageReceived(BMessage * message)1234 void ConnectDialog::MessageReceived(BMessage *message) {
1235   switch(message->what) {
1236   case MSG_OK:
1237     button = 0;
1238     break;
1239 
1240   case MSG_CANCEL:
1241     button = 1;
1242     break;
1243 
1244   default:
1245     return;
1246   }
1247 
1248   PostMessage(B_QUIT_REQUESTED);
1249 }
1250 
beos_dialog_password(const char * msg)1251 char *beos_dialog_password(const char *msg) {
1252   ConnectDialog *dialog = new ConnectDialog();
1253   dialog->Position(dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL))));
1254 
1255   if (dialog->Go()) {
1256     return strdup(dialog->text->Text());
1257   } else {
1258     return NULL;
1259   }
1260 }
1261 
beos_dialog_okcancel(const char * msg)1262 int beos_dialog_okcancel(const char *msg) {
1263   BAlert *alert = new BAlert("Dialog", msg, "OK", "Cencel");
1264 
1265   if (alert->Go() == 0) {
1266     return 1;
1267   } else {
1268     return 0;
1269   }
1270 }
1271 
beos_dialog_alert(const char * msg)1272 int beos_dialog_alert(const char *msg) {
1273   BAlert *alert = new BAlert("Alert", msg, "OK");
1274 
1275   alert->Go();
1276 
1277   return 1;
1278 }
1279 
beos_lock(void)1280 void beos_lock(void) {
1281   pthread_mutex_lock(&mutex);
1282 }
1283 
beos_unlock(void)1284 void beos_unlock(void) {
1285   pthread_mutex_unlock(&mutex);
1286 }
1287