1 /* Haiku window system support.  Hey, Emacs, this is -*- C++ -*-
2    Copyright (C) 2021 Free Software Foundation, Inc.
3 
4 This file is part of GNU Emacs.
5 
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
10 
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
18 
19 #include <config.h>
20 
21 #include <app/Application.h>
22 #include <app/Cursor.h>
23 #include <app/Messenger.h>
24 
25 #include <interface/GraphicsDefs.h>
26 #include <interface/InterfaceDefs.h>
27 #include <interface/Bitmap.h>
28 #include <interface/Window.h>
29 #include <interface/View.h>
30 #include <interface/Screen.h>
31 #include <interface/ScrollBar.h>
32 #include <interface/Region.h>
33 #include <interface/Menu.h>
34 #include <interface/MenuItem.h>
35 #include <interface/PopUpMenu.h>
36 #include <interface/MenuBar.h>
37 #include <interface/Alert.h>
38 #include <interface/Button.h>
39 
40 #include <locale/UnicodeChar.h>
41 
42 #include <game/WindowScreen.h>
43 #include <game/DirectWindow.h>
44 
45 #include <storage/Entry.h>
46 #include <storage/Path.h>
47 #include <storage/FilePanel.h>
48 #include <storage/AppFileInfo.h>
49 #include <storage/Path.h>
50 #include <storage/PathFinder.h>
51 
52 #include <support/Beep.h>
53 #include <support/DataIO.h>
54 #include <support/Locker.h>
55 
56 #include <translation/TranslatorRoster.h>
57 #include <translation/TranslationDefs.h>
58 #include <translation/TranslationUtils.h>
59 
60 #include <kernel/OS.h>
61 #include <kernel/fs_attr.h>
62 #include <kernel/scheduler.h>
63 
64 #include <private/interface/ToolTip.h>
65 
66 #include <cmath>
67 #include <cstring>
68 #include <cstdint>
69 #include <cstdio>
70 #include <csignal>
71 #include <cfloat>
72 
73 #include <pthread.h>
74 
75 #ifdef USE_BE_CAIRO
76 #include <cairo.h>
77 #endif
78 
79 #include "haiku_support.h"
80 
81 #define SCROLL_BAR_UPDATE 3000
82 
83 static color_space dpy_color_space = B_NO_COLOR_SPACE;
84 static key_map *key_map = NULL;
85 static char *key_chars = NULL;
86 static BLocker key_map_lock;
87 
88 /* The locking semantics of BWindows running in multiple threads are
89    so complex that child frame state (which is the only state that is
90    shared between different BWindows at runtime) does best with a
91    single global lock.  */
92 
93 static BLocker child_frame_lock;
94 
95 extern "C"
96 {
97   extern _Noreturn void emacs_abort (void);
98   /* Also defined in haikuterm.h.  */
99   extern void be_app_quit (void);
100 }
101 
102 static thread_id app_thread;
103 
104 _Noreturn void
gui_abort(const char * msg)105 gui_abort (const char *msg)
106 {
107   fprintf (stderr, "Abort in GUI code: %s\n", msg);
108   fprintf (stderr, "Under Haiku, Emacs cannot recover from errors in GUI code\n");
109   fprintf (stderr, "App Server disconnects usually manifest as bitmap "
110 	   "initialization failures or lock failures.");
111   emacs_abort ();
112 }
113 
114 static void
map_key(char * chars,int32 offset,uint32_t * c)115 map_key (char *chars, int32 offset, uint32_t *c)
116 {
117   int size = chars[offset++];
118   switch (size)
119     {
120     case 0:
121       break;
122 
123     case 1:
124       *c = chars[offset];
125       break;
126 
127     default:
128       {
129         char str[5];
130         int i = (size <= 4) ? size : 4;
131         strncpy (str, &(chars[offset]), i);
132         str[i] = '0';
133 	*c = BUnicodeChar::FromUTF8 ((char *) &str);
134 	break;
135       }
136     }
137 }
138 
139 static void
map_shift(uint32_t kc,uint32_t * ch)140 map_shift (uint32_t kc, uint32_t *ch)
141 {
142   if (!key_map_lock.Lock ())
143     gui_abort ("Failed to lock keymap");
144   if (!key_map)
145     get_key_map (&key_map, &key_chars);
146   if (!key_map)
147     return;
148   if (kc >= 128)
149     return;
150 
151   int32_t m = key_map->shift_map[kc];
152   map_key (key_chars, m, ch);
153   key_map_lock.Unlock ();
154 }
155 
156 static void
map_normal(uint32_t kc,uint32_t * ch)157 map_normal (uint32_t kc, uint32_t *ch)
158 {
159   if (!key_map_lock.Lock ())
160     gui_abort ("Failed to lock keymap");
161   if (!key_map)
162     get_key_map (&key_map, &key_chars);
163   if (!key_map)
164     return;
165   if (kc >= 128)
166     return;
167 
168   int32_t m = key_map->normal_map[kc];
169   map_key (key_chars, m, ch);
170   key_map_lock.Unlock ();
171 }
172 
173 class Emacs : public BApplication
174 {
175 public:
Emacs()176   Emacs () : BApplication ("application/x-vnd.GNU-emacs")
177   {
178   }
179 
180   void
AboutRequested(void)181   AboutRequested (void)
182   {
183     BAlert *about = new BAlert (PACKAGE_NAME,
184 				PACKAGE_STRING
185 				"\nThe extensible, self-documenting, real-time display editor.",
186 				"Close");
187     about->Go ();
188   }
189 
190   bool
QuitRequested(void)191   QuitRequested (void)
192   {
193     struct haiku_app_quit_requested_event rq;
194     haiku_write (APP_QUIT_REQUESTED_EVENT, &rq);
195     return 0;
196   }
197 
198   void
RefsReceived(BMessage * msg)199   RefsReceived (BMessage *msg)
200   {
201     struct haiku_refs_event rq;
202     entry_ref ref;
203     BEntry entry;
204     BPath path;
205     int32 cookie = 0;
206     int32 x, y;
207     void *window;
208 
209     if ((msg->FindPointer ("window", 0, &window) != B_OK)
210 	|| (msg->FindInt32 ("x", 0, &x) != B_OK)
211 	|| (msg->FindInt32 ("y", 0, &y) != B_OK))
212       return;
213 
214     rq.window = window;
215     rq.x = x;
216     rq.y = y;
217 
218     while (msg->FindRef ("refs", cookie++, &ref) == B_OK)
219       {
220         if (entry.SetTo (&ref, 0) == B_OK
221             && entry.GetPath (&path) == B_OK)
222           {
223             rq.ref = strdup (path.Path ());
224             haiku_write (REFS_EVENT, &rq);
225           }
226       }
227   }
228 };
229 
230 class EmacsWindow : public BWindow
231 {
232 public:
233   struct child_frame
234   {
235     struct child_frame *next;
236     int xoff, yoff;
237     EmacsWindow *window;
238   } *subset_windows = NULL;
239 
240   EmacsWindow *parent = NULL;
241   BRect pre_fullscreen_rect;
242   BRect pre_zoom_rect;
243   int x_before_zoom = INT_MIN;
244   int y_before_zoom = INT_MIN;
245   int fullscreen_p = 0;
246   int zoomed_p = 0;
247   int shown_flag = 0;
248 
EmacsWindow()249   EmacsWindow () : BWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK,
250 			    B_NORMAL_WINDOW_FEEL, B_NO_SERVER_SIDE_WINDOW_MODIFIERS)
251   {
252 
253   }
254 
~EmacsWindow()255   ~EmacsWindow ()
256   {
257     if (!child_frame_lock.Lock ())
258       gui_abort ("Failed to lock child frame state lock");
259     struct child_frame *next;
260     for (struct child_frame *f = subset_windows; f; f = next)
261       {
262 	f->window->Unparent ();
263 	next = f->next;
264 	delete f;
265       }
266 
267     if (this->parent)
268       UnparentAndUnlink ();
269     child_frame_lock.Unlock ();
270   }
271 
272   void
UpwardsSubset(EmacsWindow * w)273   UpwardsSubset (EmacsWindow *w)
274   {
275     for (; w; w = w->parent)
276       AddToSubset (w);
277   }
278 
279   void
UpwardsSubsetChildren(EmacsWindow * w)280   UpwardsSubsetChildren (EmacsWindow *w)
281   {
282     if (!child_frame_lock.Lock ())
283       gui_abort ("Failed to lock child frame state lock");
284     UpwardsSubset (w);
285     for (struct child_frame *f = subset_windows; f;
286 	 f = f->next)
287       f->window->UpwardsSubsetChildren (w);
288     child_frame_lock.Unlock ();
289   }
290 
291   void
UpwardsUnSubset(EmacsWindow * w)292   UpwardsUnSubset (EmacsWindow *w)
293   {
294     for (; w; w = w->parent)
295       RemoveFromSubset (w);
296   }
297 
298   void
UpwardsUnSubsetChildren(EmacsWindow * w)299   UpwardsUnSubsetChildren (EmacsWindow *w)
300   {
301     if (!child_frame_lock.Lock ())
302       gui_abort ("Failed to lock child frame state lock");
303     UpwardsUnSubset (w);
304     for (struct child_frame *f = subset_windows; f;
305 	 f = f->next)
306       f->window->UpwardsUnSubsetChildren (w);
307     child_frame_lock.Unlock ();
308   }
309 
310   void
Unparent(void)311   Unparent (void)
312   {
313     if (!child_frame_lock.Lock ())
314       gui_abort ("Failed to lock child frame state lock");
315     this->SetFeel (B_NORMAL_WINDOW_FEEL);
316     UpwardsUnSubsetChildren (parent);
317     this->RemoveFromSubset (this);
318     this->parent = NULL;
319     if (fullscreen_p)
320       {
321 	fullscreen_p = 0;
322 	MakeFullscreen (1);
323       }
324     child_frame_lock.Unlock ();
325   }
326 
327   void
UnparentAndUnlink(void)328   UnparentAndUnlink (void)
329   {
330     if (!child_frame_lock.Lock ())
331       gui_abort ("Failed to lock child frame state lock");
332     this->parent->UnlinkChild (this);
333     this->Unparent ();
334     child_frame_lock.Unlock ();
335   }
336 
337   void
UnlinkChild(EmacsWindow * window)338   UnlinkChild (EmacsWindow *window)
339   {
340     struct child_frame *last = NULL;
341     struct child_frame *tem = subset_windows;
342 
343     for (; tem; last = tem, tem = tem->next)
344       {
345 	if (tem->window == window)
346 	  {
347 	    if (last)
348 	      last->next = tem->next;
349 	    else
350 	      subset_windows = tem->next;
351 	    delete tem;
352 	    return;
353 	  }
354       }
355 
356     gui_abort ("Failed to unlink child frame");
357   }
358 
359   void
ParentTo(EmacsWindow * window)360   ParentTo (EmacsWindow *window)
361   {
362     if (!child_frame_lock.Lock ())
363       gui_abort ("Failed to lock child frame state lock");
364 
365     if (this->parent)
366       UnparentAndUnlink ();
367 
368     this->parent = window;
369     this->SetFeel (B_FLOATING_SUBSET_WINDOW_FEEL);
370     this->AddToSubset (this);
371     if (!IsHidden () && this->parent)
372       UpwardsSubsetChildren (parent);
373     if (fullscreen_p)
374       {
375 	fullscreen_p = 0;
376 	MakeFullscreen (1);
377       }
378     this->Sync ();
379     window->LinkChild (this);
380 
381     child_frame_lock.Unlock ();
382   }
383 
384   void
LinkChild(EmacsWindow * window)385   LinkChild (EmacsWindow *window)
386   {
387     struct child_frame *f = new struct child_frame;
388 
389     for (struct child_frame *f = subset_windows; f;
390 	 f = f->next)
391       {
392 	if (window == f->window)
393 	  gui_abort ("Trying to link a child frame that is already present");
394       }
395 
396     f->window = window;
397     f->next = subset_windows;
398     f->xoff = -1;
399     f->yoff = -1;
400 
401     subset_windows = f;
402   }
403 
404   void
DoMove(struct child_frame * f)405   DoMove (struct child_frame *f)
406   {
407     BRect frame = this->Frame ();
408     if (!f->window->LockLooper ())
409       gui_abort ("Failed to lock child frame window for move");
410     f->window->MoveTo (frame.left + f->xoff,
411 		       frame.top + f->yoff);
412     f->window->UnlockLooper ();
413   }
414 
415   void
DoUpdateWorkspace(struct child_frame * f)416   DoUpdateWorkspace (struct child_frame *f)
417   {
418     f->window->SetWorkspaces (this->Workspaces ());
419   }
420 
421   void
MoveChild(EmacsWindow * window,int xoff,int yoff,int weak_p)422   MoveChild (EmacsWindow *window, int xoff, int yoff,
423 	     int weak_p)
424   {
425     if (!child_frame_lock.Lock ())
426       gui_abort ("Failed to lock child frame state lock");
427 
428     for (struct child_frame *f = subset_windows; f;
429 	 f = f->next)
430       {
431 	if (window == f->window)
432 	  {
433 	    f->xoff = xoff;
434 	    f->yoff = yoff;
435 	    if (!weak_p)
436 	      DoMove (f);
437 
438 	    child_frame_lock.Unlock ();
439 	    return;
440 	  }
441       }
442 
443     child_frame_lock.Unlock ();
444     gui_abort ("Trying to move a child frame that doesn't exist");
445   }
446 
447   void
WindowActivated(bool activated)448   WindowActivated (bool activated)
449   {
450     struct haiku_activation_event rq;
451     rq.window = this;
452     rq.activated_p = activated;
453 
454     haiku_write (ACTIVATION, &rq);
455   }
456 
457   void
MessageReceived(BMessage * msg)458   MessageReceived (BMessage *msg)
459   {
460     int32 old_what = 0;
461 
462     if (msg->WasDropped ())
463       {
464 	entry_ref ref;
465 	BPoint whereto;
466 
467         if (msg->FindRef ("refs", &ref) == B_OK)
468 	  {
469 	    msg->what = B_REFS_RECEIVED;
470 	    msg->AddPointer ("window", this);
471 	    if (msg->FindPoint ("_drop_point_", &whereto) == B_OK)
472 	      {
473 		this->ConvertFromScreen (&whereto);
474 		msg->AddInt32 ("x", whereto.x);
475 		msg->AddInt32 ("y", whereto.y);
476 	      }
477 	    be_app->PostMessage (msg);
478 	    msg->SendReply (B_OK);
479 	  }
480       }
481     else if (msg->GetPointer ("menuptr"))
482       {
483 	struct haiku_menu_bar_select_event rq;
484 	rq.window = this;
485 	rq.ptr = (void *) msg->GetPointer ("menuptr");
486 	haiku_write (MENU_BAR_SELECT_EVENT, &rq);
487       }
488     else if (msg->what == 'FPSE'
489 	     || ((msg->FindInt32 ("old_what", &old_what) == B_OK
490 		  && old_what == 'FPSE')))
491       {
492 	struct haiku_file_panel_event rq;
493 	BEntry entry;
494 	BPath path;
495 	entry_ref ref;
496 
497 	rq.ptr = NULL;
498 
499 	if (msg->FindRef ("refs", &ref) == B_OK &&
500 	    entry.SetTo (&ref, 0) == B_OK &&
501 	    entry.GetPath (&path) == B_OK)
502 	  {
503 	    const char *str_path = path.Path ();
504 	    if (str_path)
505 	      rq.ptr = strdup (str_path);
506 	  }
507 
508 	if (msg->FindRef ("directory", &ref),
509 	    entry.SetTo (&ref, 0) == B_OK &&
510 	    entry.GetPath (&path) == B_OK)
511 	  {
512 	    const char *name = msg->GetString ("name");
513 	    const char *str_path = path.Path ();
514 
515 	    if (name)
516 	      {
517 		char str_buf[std::strlen (str_path)
518 			     + std::strlen (name) + 2];
519 		snprintf ((char *) &str_buf,
520 			  std::strlen (str_path)
521 			  + std::strlen (name) + 2, "%s/%s",
522 			  str_path, name);
523 		rq.ptr = strdup (str_buf);
524 	      }
525 	  }
526 
527 	haiku_write (FILE_PANEL_EVENT, &rq);
528       }
529     else
530       BWindow::MessageReceived (msg);
531   }
532 
533   void
DispatchMessage(BMessage * msg,BHandler * handler)534   DispatchMessage (BMessage *msg, BHandler *handler)
535   {
536     if (msg->what == B_KEY_DOWN || msg->what == B_KEY_UP)
537       {
538 	struct haiku_key_event rq;
539 	rq.window = this;
540 
541 	int32_t code = msg->GetInt32 ("raw_char", 0);
542 
543 	rq.modifiers = 0;
544 	uint32_t mods = modifiers ();
545 
546 	if (mods & B_SHIFT_KEY)
547 	  rq.modifiers |= HAIKU_MODIFIER_SHIFT;
548 
549 	if (mods & B_CONTROL_KEY)
550 	  rq.modifiers |= HAIKU_MODIFIER_CTRL;
551 
552 	if (mods & B_COMMAND_KEY)
553 	  rq.modifiers |= HAIKU_MODIFIER_ALT;
554 
555 	if (mods & B_OPTION_KEY)
556 	  rq.modifiers |= HAIKU_MODIFIER_SUPER;
557 
558 	rq.mb_char = code;
559 	rq.kc = msg->GetInt32 ("key", -1);
560 	rq.unraw_mb_char =
561 	  BUnicodeChar::FromUTF8 (msg->GetString ("bytes"));
562 
563 	if ((mods & B_SHIFT_KEY) && rq.kc >= 0)
564 	  map_shift (rq.kc, &rq.unraw_mb_char);
565 	else if (rq.kc >= 0)
566 	  map_normal (rq.kc, &rq.unraw_mb_char);
567 
568 	haiku_write (msg->what == B_KEY_DOWN ? KEY_DOWN : KEY_UP, &rq);
569       }
570     else if (msg->what == B_MOUSE_WHEEL_CHANGED)
571       {
572 	struct haiku_wheel_move_event rq;
573 	rq.window = this;
574 	rq.modifiers = 0;
575 
576 	uint32_t mods = modifiers ();
577 
578 	if (mods & B_SHIFT_KEY)
579 	  rq.modifiers |= HAIKU_MODIFIER_SHIFT;
580 
581 	if (mods & B_CONTROL_KEY)
582 	  rq.modifiers |= HAIKU_MODIFIER_CTRL;
583 
584 	if (mods & B_COMMAND_KEY)
585 	  rq.modifiers |= HAIKU_MODIFIER_ALT;
586 
587 	if (mods & B_OPTION_KEY)
588 	  rq.modifiers |= HAIKU_MODIFIER_SUPER;
589 
590 	float dx, dy;
591 	if (msg->FindFloat ("be:wheel_delta_x", &dx) == B_OK &&
592 	    msg->FindFloat ("be:wheel_delta_y", &dy) == B_OK)
593 	  {
594 	    rq.delta_x = dx;
595 	    rq.delta_y = dy;
596 
597 	    haiku_write (WHEEL_MOVE_EVENT, &rq);
598 	  };
599       }
600     else
601       BWindow::DispatchMessage (msg, handler);
602   }
603 
604   void
MenusBeginning()605   MenusBeginning ()
606   {
607     struct haiku_menu_bar_state_event rq;
608     rq.window = this;
609 
610     haiku_write (MENU_BAR_OPEN, &rq);
611   }
612 
613   void
MenusEnded()614   MenusEnded ()
615   {
616     struct haiku_menu_bar_state_event rq;
617     rq.window = this;
618 
619     haiku_write (MENU_BAR_CLOSE, &rq);
620   }
621 
622   void
FrameResized(float newWidth,float newHeight)623   FrameResized (float newWidth, float newHeight)
624   {
625     struct haiku_resize_event rq;
626     rq.window = this;
627     rq.px_heightf = newHeight + 1.0f;
628     rq.px_widthf = newWidth + 1.0f;
629 
630     haiku_write (FRAME_RESIZED, &rq);
631     BWindow::FrameResized (newWidth, newHeight);
632   }
633 
634   void
FrameMoved(BPoint newPosition)635   FrameMoved (BPoint newPosition)
636   {
637     struct haiku_move_event rq;
638     rq.window = this;
639     rq.x = std::lrint (newPosition.x);
640     rq.y = std::lrint (newPosition.y);
641 
642     haiku_write (MOVE_EVENT, &rq);
643 
644     if (!child_frame_lock.Lock ())
645       gui_abort ("Failed to lock child frame state lock");
646 
647     for (struct child_frame *f = subset_windows;
648 	 f; f = f->next)
649       DoMove (f);
650 
651     child_frame_lock.Unlock ();
652     BWindow::FrameMoved (newPosition);
653   }
654 
655   void
WorkspacesChanged(uint32_t old,uint32_t n)656   WorkspacesChanged (uint32_t old, uint32_t n)
657   {
658     for (struct child_frame *f = subset_windows;
659 	 f; f = f->next)
660       DoUpdateWorkspace (f);
661   }
662 
663   void
EmacsMoveTo(int x,int y)664   EmacsMoveTo (int x, int y)
665   {
666     if (!child_frame_lock.Lock ())
667       gui_abort ("Failed to lock child frame state lock");
668 
669     if (!this->parent)
670       this->MoveTo (x, y);
671     else
672       this->parent->MoveChild (this, x, y, 0);
673     child_frame_lock.Unlock ();
674   }
675 
676   bool
QuitRequested()677   QuitRequested ()
678   {
679     struct haiku_quit_requested_event rq;
680     rq.window = this;
681     haiku_write (QUIT_REQUESTED, &rq);
682     return false;
683   }
684 
685   void
Minimize(bool minimized_p)686   Minimize (bool minimized_p)
687   {
688     BWindow::Minimize (minimized_p);
689     struct haiku_iconification_event rq;
690     rq.window = this;
691     rq.iconified_p = !parent && minimized_p;
692 
693     haiku_write (ICONIFICATION, &rq);
694   }
695 
696   void
EmacsHide(void)697   EmacsHide (void)
698   {
699     if (this->IsHidden ())
700       return;
701     if (!child_frame_lock.Lock ())
702       gui_abort ("Failed to lock child frame state lock");
703 
704     Hide ();
705     if (this->parent)
706       UpwardsUnSubsetChildren (this->parent);
707 
708     child_frame_lock.Unlock ();
709   }
710 
711   void
EmacsShow(void)712   EmacsShow (void)
713   {
714     if (!this->IsHidden ())
715       return;
716 
717     if (!child_frame_lock.Lock ())
718       gui_abort ("Failed to lock child frame state lock");
719 
720     if (this->parent)
721       shown_flag = 1;
722     Show ();
723     if (this->parent)
724       UpwardsSubsetChildren (this->parent);
725 
726     child_frame_lock.Unlock ();
727   }
728 
729   void
Zoom(BPoint o,float w,float h)730   Zoom (BPoint o, float w, float h)
731   {
732     struct haiku_zoom_event rq;
733     rq.window = this;
734 
735     rq.x = o.x;
736     rq.y = o.y;
737 
738     rq.width = w + 1;
739     rq.height = h + 1;
740 
741     if (fullscreen_p)
742       MakeFullscreen (0);
743 
744     if (o.x != x_before_zoom ||
745 	o.y != y_before_zoom)
746       {
747 	x_before_zoom = Frame ().left;
748 	y_before_zoom = Frame ().top;
749 	pre_zoom_rect = Frame ();
750 	zoomed_p = 1;
751 	haiku_write (ZOOM_EVENT, &rq);
752       }
753     else
754       {
755 	zoomed_p = 0;
756 	x_before_zoom = y_before_zoom = INT_MIN;
757       }
758 
759     BWindow::Zoom (o, w, h);
760   }
761 
762   void
UnZoom(void)763   UnZoom (void)
764   {
765     if (!zoomed_p)
766       return;
767     zoomed_p = 0;
768 
769     EmacsMoveTo (pre_zoom_rect.left, pre_zoom_rect.top);
770     ResizeTo (pre_zoom_rect.Width (),
771 	      pre_zoom_rect.Height ());
772   }
773 
774   void
GetParentWidthHeight(int * width,int * height)775   GetParentWidthHeight (int *width, int *height)
776   {
777     if (!child_frame_lock.Lock ())
778       gui_abort ("Failed to lock child frame state lock");
779 
780     if (parent)
781       {
782 	*width = parent->Frame ().Width ();
783 	*height = parent->Frame ().Height ();
784       }
785     else
786       {
787 	BScreen s (this);
788 	*width = s.Frame ().Width ();
789 	*height = s.Frame ().Height ();
790       }
791 
792     child_frame_lock.Unlock ();
793   }
794 
795   void
OffsetChildRect(BRect * r,EmacsWindow * c)796   OffsetChildRect (BRect *r, EmacsWindow *c)
797   {
798     if (!child_frame_lock.Lock ())
799       gui_abort ("Failed to lock child frame state lock");
800 
801     for (struct child_frame *f; f; f = f->next)
802       if (f->window == c)
803 	{
804 	  r->top -= f->yoff;
805 	  r->bottom -= f->yoff;
806 	  r->left -= f->xoff;
807 	  r->right -= f->xoff;
808 	  child_frame_lock.Unlock ();
809 	  return;
810 	}
811 
812     child_frame_lock.Lock ();
813     gui_abort ("Trying to calculate offsets for a child frame that doesn't exist");
814   }
815 
816   void
MakeFullscreen(int make_fullscreen_p)817   MakeFullscreen (int make_fullscreen_p)
818   {
819     BScreen screen (this);
820 
821     if (!screen.IsValid ())
822       gui_abort ("Trying to make a window fullscreen without a screen");
823 
824     if (make_fullscreen_p == fullscreen_p)
825       return;
826 
827     fullscreen_p = make_fullscreen_p;
828     uint32 flags = Flags ();
829     if (fullscreen_p)
830       {
831 	if (zoomed_p)
832 	  UnZoom ();
833 
834 	flags |= B_NOT_MOVABLE | B_NOT_ZOOMABLE;
835 	pre_fullscreen_rect = Frame ();
836 
837 	if (!child_frame_lock.Lock ())
838 	  gui_abort ("Failed to lock child frame state lock");
839 
840 	if (parent)
841 	  parent->OffsetChildRect (&pre_fullscreen_rect, this);
842 
843 	child_frame_lock.Unlock ();
844 
845 	int w, h;
846 	EmacsMoveTo (0, 0);
847 	GetParentWidthHeight (&w, &h);
848 	ResizeTo (w, h);
849       }
850     else
851       {
852 	flags &= ~(B_NOT_MOVABLE | B_NOT_ZOOMABLE);
853 	EmacsMoveTo (pre_fullscreen_rect.left,
854 		     pre_fullscreen_rect.top);
855 	ResizeTo (pre_fullscreen_rect.Width (),
856 		  pre_fullscreen_rect.Height ());
857       }
858     SetFlags (flags);
859   }
860 };
861 
862 class EmacsMenuBar : public BMenuBar
863 {
864 public:
EmacsMenuBar()865   EmacsMenuBar () : BMenuBar (BRect (0, 0, 0, 0), NULL)
866   {
867   }
868 
869   void
FrameResized(float newWidth,float newHeight)870   FrameResized (float newWidth, float newHeight)
871   {
872     struct haiku_menu_bar_resize_event rq;
873     rq.window = this->Window ();
874     rq.height = std::lrint (newHeight);
875     rq.width = std::lrint (newWidth);
876 
877     haiku_write (MENU_BAR_RESIZE, &rq);
878     BMenuBar::FrameResized (newWidth, newHeight);
879   }
880 };
881 
882 class EmacsView : public BView
883 {
884 public:
885   uint32_t visible_bell_color = 0;
886   uint32_t previous_buttons = 0;
887   int looper_locked_count = 0;
888   BRegion sb_region;
889 
890   BView *offscreen_draw_view = NULL;
891   BBitmap *offscreen_draw_bitmap_1 = NULL;
892   BBitmap *copy_bitmap = NULL;
893 
894 #ifdef USE_BE_CAIRO
895   cairo_surface_t *cr_surface = NULL;
896   BLocker cr_surface_lock;
897 #endif
898 
899   BPoint tt_absl_pos;
900 
901   color_space cspace;
902 
EmacsView()903   EmacsView () : BView (BRect (0, 0, 0, 0), "Emacs", B_FOLLOW_NONE, B_WILL_DRAW)
904   {
905 
906   }
907 
~EmacsView()908   ~EmacsView ()
909   {
910     TearDownDoubleBuffering ();
911   }
912 
913   void
AttachedToWindow(void)914   AttachedToWindow (void)
915   {
916     cspace = B_RGBA32;
917   }
918 
919 #ifdef USE_BE_CAIRO
920   void
DetachCairoSurface(void)921   DetachCairoSurface (void)
922   {
923     if (!cr_surface_lock.Lock ())
924       gui_abort ("Could not lock cr surface during detachment");
925     if (!cr_surface)
926       gui_abort ("Trying to detach window cr surface when none exists");
927     cairo_surface_destroy (cr_surface);
928     cr_surface = NULL;
929     cr_surface_lock.Unlock ();
930   }
931 
932   void
AttachCairoSurface(void)933   AttachCairoSurface (void)
934   {
935     if (!cr_surface_lock.Lock ())
936       gui_abort ("Could not lock cr surface during attachment");
937     if (cr_surface)
938       gui_abort ("Trying to attach cr surface when one already exists");
939     cr_surface = cairo_image_surface_create_for_data
940       ((unsigned char *) offscreen_draw_bitmap_1->Bits (),
941        CAIRO_FORMAT_ARGB32, offscreen_draw_bitmap_1->Bounds ().Width (),
942        offscreen_draw_bitmap_1->Bounds ().Height (),
943        offscreen_draw_bitmap_1->BytesPerRow ());
944     if (!cr_surface)
945       gui_abort ("Cr surface allocation failed for double-buffered view");
946     cr_surface_lock.Unlock ();
947   }
948 #endif
949 
950   void
TearDownDoubleBuffering(void)951   TearDownDoubleBuffering (void)
952   {
953     if (offscreen_draw_view)
954       {
955 	if (Window ())
956 	  ClearViewBitmap ();
957 	if (copy_bitmap)
958 	  {
959 	    delete copy_bitmap;
960 	    copy_bitmap = NULL;
961 	  }
962 	if (!looper_locked_count)
963 	  if (!offscreen_draw_view->LockLooper ())
964 	    gui_abort ("Failed to lock offscreen draw view");
965 #ifdef USE_BE_CAIRO
966 	if (cr_surface)
967 	  DetachCairoSurface ();
968 #endif
969 	offscreen_draw_view->RemoveSelf ();
970 	delete offscreen_draw_view;
971 	offscreen_draw_view = NULL;
972 	delete offscreen_draw_bitmap_1;
973 	offscreen_draw_bitmap_1 = NULL;
974       }
975    }
976 
977   void
AfterResize(void)978   AfterResize (void)
979   {
980     if (offscreen_draw_view)
981       {
982 	if (!LockLooper ())
983 	  gui_abort ("Failed to lock looper after resize");
984 
985 	if (!offscreen_draw_view->LockLooper ())
986 	  gui_abort ("Failed to lock offscreen draw view after resize");
987 #ifdef USE_BE_CAIRO
988 	DetachCairoSurface ();
989 #endif
990 	offscreen_draw_view->RemoveSelf ();
991 	delete offscreen_draw_bitmap_1;
992 	offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
993 	if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
994 	  gui_abort ("Offscreen draw bitmap initialization failed");
995 
996 	offscreen_draw_view->MoveTo (Frame ().left, Frame ().top);
997 	offscreen_draw_view->ResizeTo (Frame ().Width (), Frame ().Height ());
998 	offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
999 #ifdef USE_BE_CAIRO
1000 	AttachCairoSurface ();
1001 #endif
1002 
1003 	if (looper_locked_count)
1004 	  {
1005 	    offscreen_draw_bitmap_1->Lock ();
1006 	  }
1007 
1008 	UnlockLooper ();
1009       }
1010   }
1011 
1012   void
Pulse(void)1013   Pulse (void)
1014   {
1015     visible_bell_color = 0;
1016     SetFlags (Flags () & ~B_PULSE_NEEDED);
1017     Window ()->SetPulseRate (0);
1018     Invalidate ();
1019   }
1020 
1021   void
Draw(BRect expose_bounds)1022   Draw (BRect expose_bounds)
1023   {
1024     struct haiku_expose_event rq;
1025     EmacsWindow *w = (EmacsWindow *) Window ();
1026 
1027     if (visible_bell_color > 0)
1028       {
1029 	PushState ();
1030 	BView_SetHighColorForVisibleBell (this, visible_bell_color);
1031 	FillRect (Frame ());
1032 	PopState ();
1033 	return;
1034       }
1035 
1036     if (w->shown_flag)
1037       {
1038 	PushState ();
1039 	SetDrawingMode (B_OP_ERASE);
1040 	FillRect (Frame ());
1041 	PopState ();
1042 	return;
1043       }
1044 
1045     if (!offscreen_draw_view)
1046       {
1047 	if (sb_region.Contains (std::lrint (expose_bounds.left),
1048 				std::lrint (expose_bounds.top)) &&
1049 	    sb_region.Contains (std::lrint (expose_bounds.right),
1050 				std::lrint (expose_bounds.top)) &&
1051 	    sb_region.Contains (std::lrint (expose_bounds.left),
1052 				std::lrint (expose_bounds.bottom)) &&
1053 	    sb_region.Contains (std::lrint (expose_bounds.right),
1054 				std::lrint (expose_bounds.bottom)))
1055 	  return;
1056 
1057 	rq.x = std::floor (expose_bounds.left);
1058 	rq.y = std::floor (expose_bounds.top);
1059 	rq.width = std::ceil (expose_bounds.right - expose_bounds.left + 1);
1060 	rq.height = std::ceil (expose_bounds.bottom - expose_bounds.top + 1);
1061 	if (!rq.width)
1062 	  rq.width = 1;
1063 	if (!rq.height)
1064 	  rq.height = 1;
1065 	rq.window = this->Window ();
1066 
1067 	haiku_write (FRAME_EXPOSED, &rq);
1068       }
1069   }
1070 
1071   void
DoVisibleBell(uint32_t color)1072   DoVisibleBell (uint32_t color)
1073   {
1074     if (!LockLooper ())
1075       gui_abort ("Failed to lock looper during visible bell");
1076     visible_bell_color = color | (255 << 24);
1077     SetFlags (Flags () | B_PULSE_NEEDED);
1078     Window ()->SetPulseRate (100 * 1000);
1079     Invalidate ();
1080     UnlockLooper ();
1081   }
1082 
1083   void
FlipBuffers(void)1084   FlipBuffers (void)
1085   {
1086     if (!LockLooper ())
1087       gui_abort ("Failed to lock looper during buffer flip");
1088     if (!offscreen_draw_view)
1089       gui_abort ("Failed to lock offscreen view during buffer flip");
1090 
1091     offscreen_draw_view->Flush ();
1092     offscreen_draw_view->Sync ();
1093 
1094     EmacsWindow *w = (EmacsWindow *) Window ();
1095     w->shown_flag = 0;
1096 
1097     if (copy_bitmap &&
1098 	copy_bitmap->Bounds () != offscreen_draw_bitmap_1->Bounds ())
1099       {
1100 	delete copy_bitmap;
1101 	copy_bitmap = NULL;
1102       }
1103     if (!copy_bitmap)
1104       copy_bitmap = new BBitmap (offscreen_draw_bitmap_1);
1105     else
1106       copy_bitmap->ImportBits (offscreen_draw_bitmap_1);
1107 
1108     if (copy_bitmap->InitCheck () != B_OK)
1109       gui_abort ("Failed to init copy bitmap during buffer flip");
1110 
1111     SetViewBitmap (copy_bitmap,
1112 		   Frame (), Frame (), B_FOLLOW_NONE, 0);
1113 
1114     Invalidate ();
1115     UnlockLooper ();
1116     return;
1117   }
1118 
1119   void
SetUpDoubleBuffering(void)1120   SetUpDoubleBuffering (void)
1121   {
1122     if (!LockLooper ())
1123       gui_abort ("Failed to lock self setting up double buffering");
1124     if (offscreen_draw_view)
1125       gui_abort ("Failed to lock offscreen view setting up double buffering");
1126 
1127     offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
1128     if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
1129       gui_abort ("Failed to init offscreen bitmap");
1130 #ifdef USE_BE_CAIRO
1131     AttachCairoSurface ();
1132 #endif
1133     offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
1134     offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
1135 
1136     if (looper_locked_count)
1137       {
1138 	if (!offscreen_draw_bitmap_1->Lock ())
1139 	  gui_abort ("Failed to lock bitmap after double buffering was set up");
1140       }
1141 
1142     UnlockLooper ();
1143     Invalidate ();
1144   }
1145 
1146   void
MouseMoved(BPoint point,uint32 transit,const BMessage * msg)1147   MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
1148   {
1149     struct haiku_mouse_motion_event rq;
1150 
1151     rq.just_exited_p = transit == B_EXITED_VIEW;
1152     rq.x = point.x;
1153     rq.y = point.y;
1154     rq.be_code = transit;
1155     rq.window = this->Window ();
1156 
1157     if (ToolTip ())
1158       ToolTip ()->SetMouseRelativeLocation (BPoint (-(point.x - tt_absl_pos.x),
1159 						    -(point.y - tt_absl_pos.y)));
1160 
1161     haiku_write (MOUSE_MOTION, &rq);
1162   }
1163 
1164   void
MouseDown(BPoint point)1165   MouseDown (BPoint point)
1166   {
1167     struct haiku_button_event rq;
1168     uint32 buttons;
1169 
1170     this->GetMouse (&point, &buttons, false);
1171 
1172     rq.window = this->Window ();
1173     rq.btn_no = 0;
1174 
1175     if (!(previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
1176 	(buttons & B_PRIMARY_MOUSE_BUTTON))
1177       rq.btn_no = 0;
1178     else if (!(previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
1179 	     (buttons & B_SECONDARY_MOUSE_BUTTON))
1180       rq.btn_no = 2;
1181     else if (!(previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
1182 	     (buttons & B_TERTIARY_MOUSE_BUTTON))
1183       rq.btn_no = 1;
1184     previous_buttons = buttons;
1185 
1186     rq.x = point.x;
1187     rq.y = point.y;
1188 
1189     uint32_t mods = modifiers ();
1190 
1191     rq.modifiers = 0;
1192     if (mods & B_SHIFT_KEY)
1193       rq.modifiers |= HAIKU_MODIFIER_SHIFT;
1194 
1195     if (mods & B_CONTROL_KEY)
1196       rq.modifiers |= HAIKU_MODIFIER_CTRL;
1197 
1198     if (mods & B_COMMAND_KEY)
1199       rq.modifiers |= HAIKU_MODIFIER_ALT;
1200 
1201     if (mods & B_OPTION_KEY)
1202       rq.modifiers |= HAIKU_MODIFIER_SUPER;
1203 
1204     SetMouseEventMask (B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
1205 
1206     haiku_write (BUTTON_DOWN, &rq);
1207   }
1208 
1209   void
MouseUp(BPoint point)1210   MouseUp (BPoint point)
1211   {
1212     struct haiku_button_event rq;
1213     uint32 buttons;
1214 
1215     this->GetMouse (&point, &buttons, false);
1216 
1217     rq.window = this->Window ();
1218     rq.btn_no = 0;
1219 
1220     if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON)
1221 	&& !(buttons & B_PRIMARY_MOUSE_BUTTON))
1222       rq.btn_no = 0;
1223     else if ((previous_buttons & B_SECONDARY_MOUSE_BUTTON)
1224 	     && !(buttons & B_SECONDARY_MOUSE_BUTTON))
1225       rq.btn_no = 2;
1226     else if ((previous_buttons & B_TERTIARY_MOUSE_BUTTON)
1227 	     && !(buttons & B_TERTIARY_MOUSE_BUTTON))
1228       rq.btn_no = 1;
1229     previous_buttons = buttons;
1230 
1231     rq.x = point.x;
1232     rq.y = point.y;
1233 
1234     uint32_t mods = modifiers ();
1235 
1236     rq.modifiers = 0;
1237     if (mods & B_SHIFT_KEY)
1238       rq.modifiers |= HAIKU_MODIFIER_SHIFT;
1239 
1240     if (mods & B_CONTROL_KEY)
1241       rq.modifiers |= HAIKU_MODIFIER_CTRL;
1242 
1243     if (mods & B_COMMAND_KEY)
1244       rq.modifiers |= HAIKU_MODIFIER_ALT;
1245 
1246     if (mods & B_OPTION_KEY)
1247       rq.modifiers |= HAIKU_MODIFIER_SUPER;
1248 
1249     if (!buttons)
1250       SetMouseEventMask (0, 0);
1251 
1252     haiku_write (BUTTON_UP, &rq);
1253   }
1254 };
1255 
1256 class EmacsScrollBar : public BScrollBar
1257 {
1258 public:
1259   void *scroll_bar;
1260 
EmacsScrollBar(int x,int y,int x1,int y1,bool horizontal_p)1261   EmacsScrollBar (int x, int y, int x1, int y1, bool horizontal_p) :
1262     BScrollBar (BRect (x, y, x1, y1), NULL, NULL, 0, 0, horizontal_p ?
1263 		B_HORIZONTAL : B_VERTICAL)
1264   {
1265     BView *vw = (BView *) this;
1266     vw->SetResizingMode (B_FOLLOW_NONE);
1267   }
1268 
1269   void
MessageReceived(BMessage * msg)1270   MessageReceived (BMessage *msg)
1271   {
1272     if (msg->what == SCROLL_BAR_UPDATE)
1273       {
1274 	this->SetRange (0, msg->GetInt32 ("emacs:range", 0));
1275 	this->SetValue (msg->GetInt32 ("emacs:units", 0));
1276       }
1277 
1278     BScrollBar::MessageReceived (msg);
1279   }
1280 
1281   void
ValueChanged(float new_value)1282   ValueChanged (float new_value)
1283   {
1284     struct haiku_scroll_bar_value_event rq;
1285     rq.scroll_bar = scroll_bar;
1286     rq.position = new_value;
1287 
1288     haiku_write (SCROLL_BAR_VALUE_EVENT, &rq);
1289   }
1290 
1291   void
MouseDown(BPoint pt)1292   MouseDown (BPoint pt)
1293   {
1294     struct haiku_scroll_bar_drag_event rq;
1295     rq.dragging_p = 1;
1296     rq.scroll_bar = scroll_bar;
1297 
1298     haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
1299     BScrollBar::MouseDown (pt);
1300   }
1301 
1302   void
MouseUp(BPoint pt)1303   MouseUp (BPoint pt)
1304   {
1305     struct haiku_scroll_bar_drag_event rq;
1306     rq.dragging_p = 0;
1307     rq.scroll_bar = scroll_bar;
1308 
1309     haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
1310     BScrollBar::MouseUp (pt);
1311   }
1312 };
1313 
1314 class EmacsTitleMenuItem : public BMenuItem
1315 {
1316 public:
EmacsTitleMenuItem(const char * str)1317   EmacsTitleMenuItem (const char *str) : BMenuItem (str, NULL)
1318   {
1319     SetEnabled (0);
1320   }
1321 
1322   void
DrawContent(void)1323   DrawContent (void)
1324   {
1325     BMenu *menu = Menu ();
1326 
1327     menu->PushState ();
1328     menu->SetFont (be_bold_font);
1329     BView_SetHighColorForVisibleBell (menu, 0);
1330     BMenuItem::DrawContent ();
1331     menu->PopState ();
1332   }
1333 };
1334 
1335 class EmacsMenuItem : public BMenuItem
1336 {
1337 public:
1338   int menu_bar_id = -1;
1339   void *wind_ptr = NULL;
1340   char *key = NULL;
1341   char *help = NULL;
1342 
EmacsMenuItem(const char * ky,const char * str,const char * help,BMessage * message=NULL)1343   EmacsMenuItem (const char *ky,
1344 		 const char *str,
1345 		 const char *help,
1346 		 BMessage *message = NULL) : BMenuItem (str, message)
1347   {
1348     if (ky)
1349       {
1350 	key = strdup (ky);
1351 	if (!key)
1352 	  gui_abort ("strdup failed");
1353       }
1354 
1355     if (help)
1356       {
1357 	this->help = strdup (help);
1358 	if (!this->help)
1359 	  gui_abort ("strdup failed");
1360       }
1361   }
1362 
~EmacsMenuItem()1363   ~EmacsMenuItem ()
1364   {
1365     if (key)
1366       free (key);
1367     if (help)
1368       free (help);
1369   }
1370 
1371   void
DrawContent(void)1372   DrawContent (void)
1373   {
1374     BMenu *menu = Menu ();
1375 
1376     BMenuItem::DrawContent ();
1377 
1378     if (key)
1379       {
1380 	BRect r = menu->Frame ();
1381 	int w = menu->StringWidth (key);
1382 	menu->MovePenTo (BPoint (r.Width () - w - 4,
1383 				 menu->PenLocation ().y));
1384 	menu->DrawString (key);
1385       }
1386   }
1387 
1388   void
GetContentSize(float * w,float * h)1389   GetContentSize (float *w, float *h)
1390   {
1391     BMenuItem::GetContentSize (w, h);
1392     if (Menu () && key)
1393       *w += 4 + Menu ()->StringWidth (key);
1394   }
1395 
1396   void
Highlight(bool highlight_p)1397   Highlight (bool highlight_p)
1398   {
1399     struct haiku_menu_bar_help_event rq;
1400 
1401     if (menu_bar_id >= 0)
1402       {
1403 	rq.window = wind_ptr;
1404 	rq.mb_idx = highlight_p ? menu_bar_id : -1;
1405 
1406 	haiku_write (MENU_BAR_HELP_EVENT, &rq);
1407       }
1408     else if (help)
1409       {
1410 	Menu ()->SetToolTip (highlight_p ? help : NULL);
1411       }
1412 
1413     BMenuItem::Highlight (highlight_p);
1414   }
1415 };
1416 
1417 class EmacsPopUpMenu : public BPopUpMenu
1418 {
1419 public:
EmacsPopUpMenu(const char * name)1420   EmacsPopUpMenu (const char *name) : BPopUpMenu (name, 0)
1421   {
1422 
1423   }
1424 
1425   void
FrameResized(float w,float h)1426   FrameResized (float w, float h)
1427   {
1428     Invalidate ();
1429     BPopUpMenu::FrameResized (w, h);
1430   }
1431 };
1432 
1433 static int32
start_running_application(void * data)1434 start_running_application (void *data)
1435 {
1436   haiku_io_init_in_app_thread ();
1437 
1438   if (!((Emacs *) data)->Lock ())
1439     gui_abort ("Failed to lock application");
1440 
1441   ((Emacs *) data)->Run ();
1442   ((Emacs *) data)->Unlock ();
1443   return 0;
1444 }
1445 
1446 /* Take BITMAP, a reference to a BBitmap, and return a pointer to its
1447    data.  */
1448 void *
BBitmap_data(void * bitmap)1449 BBitmap_data (void *bitmap)
1450 {
1451   return ((BBitmap *) bitmap)->Bits ();
1452 }
1453 
1454 /* Convert bitmap if required, placing the new bitmap in NEW_BITMAP,
1455    and return non-null if bitmap was successfully converted.  Bitmaps
1456    should be freed with `BBitmap_free'.  */
1457 int
BBitmap_convert(void * _bitmap,void ** new_bitmap)1458 BBitmap_convert (void *_bitmap, void **new_bitmap)
1459 {
1460   BBitmap *bitmap = (BBitmap *) _bitmap;
1461   if (bitmap->ColorSpace () == B_RGBA32)
1462     return -1;
1463   BRect bounds = bitmap->Bounds ();
1464   BBitmap *bmp = new (std::nothrow) BBitmap (bounds, B_RGBA32);
1465   if (!bmp || bmp->InitCheck () != B_OK)
1466     {
1467       if (bmp)
1468 	delete bmp;
1469       return 0;
1470     }
1471   if (bmp->ImportBits (bitmap) != B_OK)
1472     {
1473       delete bmp;
1474       return 0;
1475     }
1476   *(BBitmap **) new_bitmap = bmp;
1477   return 1;
1478 }
1479 
1480 void
BBitmap_free(void * bitmap)1481 BBitmap_free (void *bitmap)
1482 {
1483   delete (BBitmap *) bitmap;
1484 }
1485 
1486 /* Create new bitmap in RGB32 format, or in GRAY1 if MONO_P is
1487    non-zero.  */
1488 void *
BBitmap_new(int width,int height,int mono_p)1489 BBitmap_new (int width, int height, int mono_p)
1490 {
1491   BBitmap *bn = new (std::nothrow) BBitmap (BRect (0, 0, width - 1, height - 1),
1492 					    mono_p ? B_GRAY1 : B_RGB32);
1493 
1494   return bn->InitCheck () == B_OK ? (void *) bn : (void *) (delete bn, NULL);
1495 }
1496 
1497 void
BBitmap_dimensions(void * bitmap,int * left,int * top,int * right,int * bottom,int32_t * bytes_per_row,int * mono_p)1498 BBitmap_dimensions (void *bitmap, int *left, int *top,
1499 		    int *right, int *bottom,
1500 		    int32_t *bytes_per_row, int *mono_p)
1501 {
1502   BRect rect = ((BBitmap *) bitmap)->Bounds ();
1503   *left = rect.left;
1504   *top = rect.top;
1505   *right = rect.right;
1506   *bottom = rect.bottom;
1507 
1508   *bytes_per_row = ((BBitmap *) bitmap)->BytesPerRow ();
1509   *mono_p = (((BBitmap *) bitmap)->ColorSpace () == B_GRAY1);
1510 }
1511 
1512 /* Set up an application and return it.  If starting the application
1513    thread fails, abort Emacs.  */
1514 void *
BApplication_setup(void)1515 BApplication_setup (void)
1516 {
1517   if (be_app)
1518     return be_app;
1519   thread_id id;
1520   Emacs *app;
1521 
1522   app = new Emacs;
1523   app->Unlock ();
1524   if ((id = spawn_thread (start_running_application, "Emacs app thread",
1525 			  B_DEFAULT_MEDIA_PRIORITY, app)) < 0)
1526     gui_abort ("spawn_thread failed");
1527 
1528   resume_thread (id);
1529 
1530   app_thread = id;
1531   return app;
1532 }
1533 
1534 /* Set up and return a window with its view put in VIEW.  */
1535 void *
BWindow_new(void * _view)1536 BWindow_new (void *_view)
1537 {
1538   BWindow *window = new (std::nothrow) EmacsWindow;
1539   BView **v = (BView **) _view;
1540   if (!window)
1541     {
1542       *v = NULL;
1543       return window;
1544     }
1545 
1546   BView *vw = new (std::nothrow) EmacsView;
1547   if (!vw)
1548     {
1549       *v = NULL;
1550       window->Lock ();
1551       window->Quit ();
1552       return NULL;
1553     }
1554   window->AddChild (vw);
1555   *v = vw;
1556   return window;
1557 }
1558 
1559 void
BWindow_quit(void * window)1560 BWindow_quit (void *window)
1561 {
1562   ((BWindow *) window)->Lock ();
1563   ((BWindow *) window)->Quit ();
1564 }
1565 
1566 /* Set WINDOW's offset to X, Y.  */
1567 void
BWindow_set_offset(void * window,int x,int y)1568 BWindow_set_offset (void *window, int x, int y)
1569 {
1570   BWindow *wn = (BWindow *) window;
1571   EmacsWindow *w = dynamic_cast<EmacsWindow *> (wn);
1572   if (w)
1573     {
1574       if (!w->LockLooper ())
1575 	gui_abort ("Failed to lock window looper setting offset");
1576       w->EmacsMoveTo (x, y);
1577       w->UnlockLooper ();
1578     }
1579   else
1580     wn->MoveTo (x, y);
1581 }
1582 
1583 /* Iconify WINDOW.  */
1584 void
BWindow_iconify(void * window)1585 BWindow_iconify (void *window)
1586 {
1587   if (((BWindow *) window)->IsHidden ())
1588     BWindow_set_visible (window, true);
1589   ((BWindow *) window)->Minimize (true);
1590 }
1591 
1592 /* Show or hide WINDOW.  */
1593 void
BWindow_set_visible(void * window,int visible_p)1594 BWindow_set_visible (void *window, int visible_p)
1595 {
1596   EmacsWindow *win = (EmacsWindow *) window;
1597   if (visible_p)
1598     {
1599       if (win->IsMinimized ())
1600 	win->Minimize (false);
1601       win->EmacsShow ();
1602     }
1603   else if (!win->IsHidden ())
1604     {
1605       if (win->IsMinimized ())
1606 	win->Minimize (false);
1607       win->EmacsHide ();
1608     }
1609   win->Sync ();
1610 }
1611 
1612 /* Change the title of WINDOW to the multibyte string TITLE.  */
1613 void
BWindow_retitle(void * window,const char * title)1614 BWindow_retitle (void *window, const char *title)
1615 {
1616   ((BWindow *) window)->SetTitle (title);
1617 }
1618 
1619 /* Resize WINDOW to WIDTH by HEIGHT.  */
1620 void
BWindow_resize(void * window,int width,int height)1621 BWindow_resize (void *window, int width, int height)
1622 {
1623   ((BWindow *) window)->ResizeTo (width, height);
1624 }
1625 
1626 /* Activate WINDOW, making it the subject of keyboard focus and
1627    bringing it to the front of the screen.  */
1628 void
BWindow_activate(void * window)1629 BWindow_activate (void *window)
1630 {
1631   ((BWindow *) window)->Activate ();
1632 }
1633 
1634 /* Return the pixel dimensions of the main screen in WIDTH and
1635    HEIGHT.  */
1636 void
BScreen_px_dim(int * width,int * height)1637 BScreen_px_dim (int *width, int *height)
1638 {
1639   BScreen screen;
1640   if (!screen.IsValid ())
1641     gui_abort ("Invalid screen");
1642   BRect frame = screen.Frame ();
1643 
1644   *width = frame.right - frame.left;
1645   *height = frame.bottom - frame.top;
1646 }
1647 
1648 /* Resize VIEW to WIDTH, HEIGHT.  */
1649 void
BView_resize_to(void * view,int width,int height)1650 BView_resize_to (void *view, int width, int height)
1651 {
1652   EmacsView *vw = (EmacsView *) view;
1653   if (!vw->LockLooper ())
1654     gui_abort ("Failed to lock view for resize");
1655   vw->ResizeTo (width, height);
1656   vw->AfterResize ();
1657   vw->UnlockLooper ();
1658 }
1659 
1660 void *
BCursor_create_default(void)1661 BCursor_create_default (void)
1662 {
1663   return new BCursor (B_CURSOR_ID_SYSTEM_DEFAULT);
1664 }
1665 
1666 void *
BCursor_create_modeline(void)1667 BCursor_create_modeline (void)
1668 {
1669   return new BCursor (B_CURSOR_ID_CONTEXT_MENU);
1670 }
1671 
1672 void *
BCursor_from_id(enum haiku_cursor cursor)1673 BCursor_from_id (enum haiku_cursor cursor)
1674 {
1675   return new BCursor ((enum BCursorID) cursor);
1676 }
1677 
1678 void *
BCursor_create_i_beam(void)1679 BCursor_create_i_beam (void)
1680 {
1681   return new BCursor (B_CURSOR_ID_I_BEAM);
1682 }
1683 
1684 void *
BCursor_create_progress_cursor(void)1685 BCursor_create_progress_cursor (void)
1686 {
1687   return new BCursor (B_CURSOR_ID_PROGRESS);
1688 }
1689 
1690 void *
BCursor_create_grab(void)1691 BCursor_create_grab (void)
1692 {
1693   return new BCursor (B_CURSOR_ID_GRAB);
1694 }
1695 
1696 void
BCursor_delete(void * cursor)1697 BCursor_delete (void *cursor)
1698 {
1699   delete (BCursor *) cursor;
1700 }
1701 
1702 void
BView_set_view_cursor(void * view,void * cursor)1703 BView_set_view_cursor (void *view, void *cursor)
1704 {
1705   if (!((BView *) view)->LockLooper ())
1706     gui_abort ("Failed to lock view setting cursor");
1707   ((BView *) view)->SetViewCursor ((BCursor *) cursor);
1708   ((BView *) view)->UnlockLooper ();
1709 }
1710 
1711 void
BWindow_Flush(void * window)1712 BWindow_Flush (void *window)
1713 {
1714   ((BWindow *) window)->Flush ();
1715 }
1716 
1717 /* Map the keycode KC, storing the result in CODE and 1 in
1718    NON_ASCII_P if it should be used.  */
1719 void
BMapKey(uint32_t kc,int * non_ascii_p,unsigned * code)1720 BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code)
1721 {
1722   if (*code == 10 && kc != 0x42)
1723     {
1724       *code = XK_Return;
1725       *non_ascii_p = 1;
1726       return;
1727     }
1728 
1729   switch (kc)
1730     {
1731     default:
1732       *non_ascii_p = 0;
1733       if (kc < 0xe && kc > 0x1)
1734 	{
1735 	  *code = XK_F1 + kc - 2;
1736 	  *non_ascii_p = 1;
1737 	}
1738       return;
1739     case 0x1e:
1740       *code = XK_BackSpace;
1741       break;
1742     case 0x61:
1743       *code = XK_Left;
1744       break;
1745     case 0x63:
1746       *code = XK_Right;
1747       break;
1748     case 0x57:
1749       *code = XK_Up;
1750       break;
1751     case 0x62:
1752       *code = XK_Down;
1753       break;
1754     case 0x64:
1755       *code = XK_Insert;
1756       break;
1757     case 0x65:
1758       *code = XK_Delete;
1759       break;
1760     case 0x37:
1761       *code = XK_Home;
1762       break;
1763     case 0x58:
1764       *code = XK_End;
1765       break;
1766     case 0x39:
1767       *code = XK_Page_Up;
1768       break;
1769     case 0x5a:
1770       *code = XK_Page_Down;
1771       break;
1772     case 0x1:
1773       *code = XK_Escape;
1774       break;
1775     case 0x68:
1776       *code = XK_Menu;
1777       break;
1778     }
1779   *non_ascii_p = 1;
1780 }
1781 
1782 /* Make a scrollbar, attach it to VIEW's window, and return it.  */
1783 void *
BScrollBar_make_for_view(void * view,int horizontal_p,int x,int y,int x1,int y1,void * scroll_bar_ptr)1784 BScrollBar_make_for_view (void *view, int horizontal_p,
1785 			  int x, int y, int x1, int y1,
1786 			  void *scroll_bar_ptr)
1787 {
1788   EmacsScrollBar *sb = new EmacsScrollBar (x, y, x1, y1, horizontal_p);
1789   sb->scroll_bar = scroll_bar_ptr;
1790 
1791   BView *vw = (BView *) view;
1792   BView *sv = (BView *) sb;
1793   if (!vw->LockLooper ())
1794     gui_abort ("Failed to lock scrollbar owner");
1795   vw->AddChild ((BView *) sb);
1796   sv->WindowActivated (vw->Window ()->IsActive ());
1797   vw->UnlockLooper ();
1798   return sb;
1799 }
1800 
1801 void
BScrollBar_delete(void * sb)1802 BScrollBar_delete (void *sb)
1803 {
1804   BView *view = (BView *) sb;
1805   BView *pr = view->Parent ();
1806 
1807   if (!pr->LockLooper ())
1808     gui_abort ("Failed to lock scrollbar parent");
1809   pr->RemoveChild (view);
1810   pr->UnlockLooper ();
1811 
1812   delete (EmacsScrollBar *) sb;
1813 }
1814 
1815 void
BView_move_frame(void * view,int x,int y,int x1,int y1)1816 BView_move_frame (void *view, int x, int y, int x1, int y1)
1817 {
1818   BView *vw = (BView *) view;
1819 
1820   if (!vw->LockLooper ())
1821     gui_abort ("Failed to lock view moving frame");
1822   vw->MoveTo (x, y);
1823   vw->ResizeTo (x1 - x, y1 - y);
1824   vw->Flush ();
1825   vw->Sync ();
1826   vw->UnlockLooper ();
1827 }
1828 
1829 void
BView_scroll_bar_update(void * sb,int portion,int whole,int position)1830 BView_scroll_bar_update (void *sb, int portion, int whole, int position)
1831 {
1832   BScrollBar *bar = (BScrollBar *) sb;
1833   BMessage msg = BMessage (SCROLL_BAR_UPDATE);
1834   BMessenger mr = BMessenger (bar);
1835   msg.AddInt32 ("emacs:range", whole);
1836   msg.AddInt32 ("emacs:units", position);
1837 
1838   mr.SendMessage (&msg);
1839 }
1840 
1841 /* Return the default scrollbar size.  */
1842 int
BScrollBar_default_size(int horizontal_p)1843 BScrollBar_default_size (int horizontal_p)
1844 {
1845   return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
1846 }
1847 
1848 /* Invalidate VIEW, causing it to be drawn again.  */
1849 void
BView_invalidate(void * view)1850 BView_invalidate (void *view)
1851 {
1852   BView *vw = (BView *) view;
1853   if (!vw->LockLooper ())
1854     gui_abort ("Couldn't lock view while invalidating it");
1855   vw->Invalidate ();
1856   vw->UnlockLooper ();
1857 }
1858 
1859 /* Lock VIEW in preparation for drawing operations.  This should be
1860    called before any attempt to draw onto VIEW or to lock it for Cairo
1861    drawing.  `BView_draw_unlock' should be called afterwards.  */
1862 void
BView_draw_lock(void * view)1863 BView_draw_lock (void *view)
1864 {
1865   EmacsView *vw = (EmacsView *) view;
1866   if (vw->looper_locked_count)
1867     {
1868       vw->looper_locked_count++;
1869       return;
1870     }
1871   BView *v = (BView *) find_appropriate_view_for_draw (vw);
1872   if (v != vw)
1873     {
1874       if (!vw->offscreen_draw_bitmap_1->Lock ())
1875 	gui_abort ("Failed to lock offscreen bitmap while acquiring draw lock");
1876     }
1877   else if (!v->LockLooper ())
1878     gui_abort ("Failed to lock draw view while acquiring draw lock");
1879 
1880   if (v != vw && !vw->LockLooper ())
1881     gui_abort ("Failed to lock view while acquiring draw lock");
1882   vw->looper_locked_count++;
1883 }
1884 
1885 void
BView_draw_unlock(void * view)1886 BView_draw_unlock (void *view)
1887 {
1888   EmacsView *vw = (EmacsView *) view;
1889   if (--vw->looper_locked_count)
1890     return;
1891 
1892   BView *v = (BView *) find_appropriate_view_for_draw (view);
1893   if (v == vw)
1894     vw->UnlockLooper ();
1895   else
1896     {
1897       vw->offscreen_draw_bitmap_1->Unlock ();
1898       vw->UnlockLooper ();
1899     }
1900 }
1901 
1902 void
BWindow_center_on_screen(void * window)1903 BWindow_center_on_screen (void *window)
1904 {
1905   BWindow *w = (BWindow *) window;
1906   w->CenterOnScreen ();
1907 }
1908 
1909 /* Tell VIEW it has been clicked at X by Y.  */
1910 void
BView_mouse_down(void * view,int x,int y)1911 BView_mouse_down (void *view, int x, int y)
1912 {
1913   BView *vw = (BView *) view;
1914   if (vw->LockLooper ())
1915     {
1916       vw->MouseDown (BPoint (x, y));
1917       vw->UnlockLooper ();
1918     }
1919 }
1920 
1921 /* Tell VIEW the mouse has been released at X by Y.  */
1922 void
BView_mouse_up(void * view,int x,int y)1923 BView_mouse_up (void *view, int x, int y)
1924 {
1925   BView *vw = (BView *) view;
1926   if (vw->LockLooper ())
1927     {
1928       vw->MouseUp (BPoint (x, y));
1929       vw->UnlockLooper ();
1930     }
1931 }
1932 
1933 /* Tell VIEW that the mouse has moved to Y by Y.  */
1934 void
BView_mouse_moved(void * view,int x,int y,uint32_t transit)1935 BView_mouse_moved (void *view, int x, int y, uint32_t transit)
1936 {
1937   BView *vw = (BView *) view;
1938   if (vw->LockLooper ())
1939     {
1940       vw->MouseMoved (BPoint (x, y), transit, NULL);
1941       vw->UnlockLooper ();
1942     }
1943 }
1944 
1945 /* Import BITS into BITMAP using the B_GRAY1 colorspace.  */
1946 void
BBitmap_import_mono_bits(void * bitmap,void * bits,int wd,int h)1947 BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h)
1948 {
1949   BBitmap *bmp = (BBitmap *) bitmap;
1950   unsigned char *data = (unsigned char *) bmp->Bits ();
1951   unsigned short *bts = (unsigned short *) bits;
1952 
1953   for (int i = 0; i < (h * (wd / 8)); i++)
1954     {
1955       *((unsigned short *) data) = bts[i];
1956       data += bmp->BytesPerRow ();
1957     }
1958 }
1959 
1960 /* Make a scrollbar at X, Y known to the view VIEW.  */
1961 void
BView_publish_scroll_bar(void * view,int x,int y,int width,int height)1962 BView_publish_scroll_bar (void *view, int x, int y, int width, int height)
1963 {
1964   EmacsView *vw = (EmacsView *) view;
1965   if (vw->LockLooper ())
1966     {
1967       vw->sb_region.Include (BRect (x, y, x - 1 + width,
1968 				    y - 1 + height));
1969       vw->UnlockLooper ();
1970     }
1971 }
1972 
1973 void
BView_forget_scroll_bar(void * view,int x,int y,int width,int height)1974 BView_forget_scroll_bar (void *view, int x, int y, int width, int height)
1975 {
1976   EmacsView *vw = (EmacsView *) view;
1977   if (vw->LockLooper ())
1978     {
1979       vw->sb_region.Exclude (BRect (x, y, x - 1 + width,
1980 				    y - 1 + height));
1981       vw->UnlockLooper ();
1982     }
1983 }
1984 
1985 void
BView_get_mouse(void * view,int * x,int * y)1986 BView_get_mouse (void *view, int *x, int *y)
1987 {
1988   BPoint l;
1989   BView *vw = (BView *) view;
1990   if (!vw->LockLooper ())
1991     gui_abort ("Failed to lock view in BView_get_mouse");
1992   vw->GetMouse (&l, NULL, 1);
1993   vw->UnlockLooper ();
1994 
1995   *x = std::lrint (l.x);
1996   *y = std::lrint (l.y);
1997 }
1998 
1999 /* Perform an in-place conversion of X and Y from VIEW's coordinate
2000    system to its screen's coordinate system.  */
2001 void
BView_convert_to_screen(void * view,int * x,int * y)2002 BView_convert_to_screen (void *view, int *x, int *y)
2003 {
2004   BPoint l = BPoint (*x, *y);
2005   BView *vw = (BView *) view;
2006   if (!vw->LockLooper ())
2007     gui_abort ("Failed to lock view in convert_to_screen");
2008   vw->ConvertToScreen (&l);
2009   vw->UnlockLooper ();
2010 
2011   *x = std::lrint (l.x);
2012   *y = std::lrint (l.y);
2013 }
2014 
2015 void
BView_convert_from_screen(void * view,int * x,int * y)2016 BView_convert_from_screen (void *view, int *x, int *y)
2017 {
2018   BPoint l = BPoint (*x, *y);
2019   BView *vw = (BView *) view;
2020   if (!vw->LockLooper ())
2021     gui_abort ("Failed to lock view in convert_from_screen");
2022   vw->ConvertFromScreen (&l);
2023   vw->UnlockLooper ();
2024 
2025   *x = std::lrint (l.x);
2026   *y = std::lrint (l.y);
2027 }
2028 
2029 /* Decorate or undecorate WINDOW depending on DECORATE_P.  */
2030 void
BWindow_change_decoration(void * window,int decorate_p)2031 BWindow_change_decoration (void *window, int decorate_p)
2032 {
2033   BWindow *w = (BWindow *) window;
2034   if (!w->LockLooper ())
2035     gui_abort ("Failed to lock window while changing its decorations");
2036   if (decorate_p)
2037     w->SetLook (B_TITLED_WINDOW_LOOK);
2038   else
2039     w->SetLook (B_NO_BORDER_WINDOW_LOOK);
2040   w->UnlockLooper ();
2041 }
2042 
2043 /* Decorate WINDOW appropriately for use as a tooltip.  */
2044 void
BWindow_set_tooltip_decoration(void * window)2045 BWindow_set_tooltip_decoration (void *window)
2046 {
2047   BWindow *w = (BWindow *) window;
2048   if (!w->LockLooper ())
2049     gui_abort ("Failed to lock window while setting ttip decoration");
2050   w->SetLook (B_BORDERED_WINDOW_LOOK);
2051   w->SetFeel (B_FLOATING_APP_WINDOW_FEEL);
2052   w->UnlockLooper ();
2053 }
2054 
2055 /* Set B_AVOID_FOCUS on WINDOW if AVOID_FOCUS_P is non-nil, or clear
2056    it otherwise.  */
2057 void
BWindow_set_avoid_focus(void * window,int avoid_focus_p)2058 BWindow_set_avoid_focus (void *window, int avoid_focus_p)
2059 {
2060   BWindow *w = (BWindow *) window;
2061   if (!w->LockLooper ())
2062     gui_abort ("Failed to lock window while setting avoid focus");
2063 
2064   if (!avoid_focus_p)
2065     w->SetFlags (w->Flags () & ~B_AVOID_FOCUS);
2066   else
2067     w->SetFlags (w->Flags () | B_AVOID_FOCUS);
2068   w->Sync ();
2069   w->UnlockLooper ();
2070 }
2071 
2072 void
BView_emacs_delete(void * view)2073 BView_emacs_delete (void *view)
2074 {
2075   EmacsView *vw = (EmacsView *) view;
2076   if (!vw->LockLooper ())
2077     gui_abort ("Failed to lock view while deleting it");
2078   vw->RemoveSelf ();
2079   delete vw;
2080 }
2081 
2082 /* Return the current workspace.  */
2083 uint32_t
haiku_current_workspace(void)2084 haiku_current_workspace (void)
2085 {
2086   return current_workspace ();
2087 }
2088 
2089 /* Return a bitmask consisting of workspaces WINDOW is on.  */
2090 uint32_t
BWindow_workspaces(void * window)2091 BWindow_workspaces (void *window)
2092 {
2093   return ((BWindow *) window)->Workspaces ();
2094 }
2095 
2096 /* Create a popup menu.  */
2097 void *
BPopUpMenu_new(const char * name)2098 BPopUpMenu_new (const char *name)
2099 {
2100   BPopUpMenu *menu = new EmacsPopUpMenu (name);
2101   menu->SetRadioMode (0);
2102   return menu;
2103 }
2104 
2105 /* Add a title item to MENU.  These items cannot be highlighted or
2106    triggered, and their labels will display as bold text.  */
2107 void
BMenu_add_title(void * menu,const char * text)2108 BMenu_add_title (void *menu, const char *text)
2109 {
2110   EmacsTitleMenuItem *it = new EmacsTitleMenuItem (text);
2111   BMenu *mn = (BMenu *) menu;
2112   mn->AddItem (it);
2113 }
2114 
2115 /* Add an item to the menu MENU.  */
2116 void
BMenu_add_item(void * menu,const char * label,void * ptr,bool enabled_p,bool marked_p,bool mbar_p,void * mbw_ptr,const char * key,const char * help)2117 BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
2118 		bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
2119 		const char *help)
2120 {
2121   BMenu *m = (BMenu *) menu;
2122   BMessage *msg;
2123   if (ptr)
2124     msg = new BMessage ();
2125   EmacsMenuItem *it = new EmacsMenuItem (key, label, help, ptr ? msg : NULL);
2126   it->SetTarget (m->Window ());
2127   it->SetEnabled (enabled_p);
2128   it->SetMarked (marked_p);
2129   if (mbar_p)
2130     {
2131       it->menu_bar_id = (intptr_t) ptr;
2132       it->wind_ptr = mbw_ptr;
2133     }
2134   if (ptr)
2135     msg->AddPointer ("menuptr", ptr);
2136   m->AddItem (it);
2137 }
2138 
2139 /* Add a separator to the menu MENU.  */
2140 void
BMenu_add_separator(void * menu)2141 BMenu_add_separator (void *menu)
2142 {
2143   BMenu *m = (BMenu *) menu;
2144 
2145   m->AddSeparatorItem ();
2146 }
2147 
2148 /* Create a submenu and attach it to MENU.  */
2149 void *
BMenu_new_submenu(void * menu,const char * label,bool enabled_p)2150 BMenu_new_submenu (void *menu, const char *label, bool enabled_p)
2151 {
2152   BMenu *m = (BMenu *) menu;
2153   BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
2154   mn->SetRadioMode (0);
2155   BMenuItem *i = new BMenuItem (mn);
2156   i->SetEnabled (enabled_p);
2157   m->AddItem (i);
2158   return mn;
2159 }
2160 
2161 /* Create a submenu that notifies Emacs upon opening.  */
2162 void *
BMenu_new_menu_bar_submenu(void * menu,const char * label)2163 BMenu_new_menu_bar_submenu (void *menu, const char *label)
2164 {
2165   BMenu *m = (BMenu *) menu;
2166   BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
2167   mn->SetRadioMode (0);
2168   BMenuItem *i = new BMenuItem (mn);
2169   i->SetEnabled (1);
2170   m->AddItem (i);
2171   return mn;
2172 }
2173 
2174 /* Run MENU, waiting for it to close, and return a pointer to the
2175    data of the selected item (if one exists), or NULL.  X, Y should
2176    be in the screen coordinate system.  */
2177 void *
BMenu_run(void * menu,int x,int y)2178 BMenu_run (void *menu, int x, int y)
2179 {
2180   BPopUpMenu *mn = (BPopUpMenu *) menu;
2181   mn->SetRadioMode (0);
2182   BMenuItem *it = mn->Go (BPoint (x, y));
2183   if (it)
2184     {
2185       BMessage *mg = it->Message ();
2186       if (mg)
2187 	return (void *) mg->GetPointer ("menuptr");
2188       else
2189 	return NULL;
2190     }
2191   return NULL;
2192 }
2193 
2194 /* Delete the entire menu hierarchy of MENU, and then delete MENU
2195    itself.  */
2196 void
BPopUpMenu_delete(void * menu)2197 BPopUpMenu_delete (void *menu)
2198 {
2199   delete (BPopUpMenu *) menu;
2200 }
2201 
2202 /* Create a menubar, attach it to VIEW, and return it.  */
2203 void *
BMenuBar_new(void * view)2204 BMenuBar_new (void *view)
2205 {
2206   BView *vw = (BView *) view;
2207   EmacsMenuBar *bar = new EmacsMenuBar ();
2208 
2209   if (!vw->LockLooper ())
2210     gui_abort ("Failed to lock menu bar parent");
2211   vw->AddChild ((BView *) bar);
2212   vw->UnlockLooper ();
2213 
2214   return bar;
2215 }
2216 
2217 /* Delete MENUBAR along with all subitems. */
2218 void
BMenuBar_delete(void * menubar)2219 BMenuBar_delete (void *menubar)
2220 {
2221   BView *vw = (BView *) menubar;
2222   BView *p = vw->Parent ();
2223   if (!p->LockLooper ())
2224     gui_abort ("Failed to lock menu bar parent while removing menubar");
2225   vw->RemoveSelf ();
2226   p->UnlockLooper ();
2227   delete vw;
2228 }
2229 
2230 /* Delete all items from MENU.  */
2231 void
BMenu_delete_all(void * menu)2232 BMenu_delete_all (void *menu)
2233 {
2234   BMenu *mn = (BMenu *) menu;
2235   mn->RemoveItems (0, mn->CountItems (), true);
2236 }
2237 
2238 /* Delete COUNT items from MENU starting from START.  */
2239 void
BMenu_delete_from(void * menu,int start,int count)2240 BMenu_delete_from (void *menu, int start, int count)
2241 {
2242   BMenu *mn = (BMenu *) menu;
2243   mn->RemoveItems (start, count, true);
2244 }
2245 
2246 /* Count items in menu MENU.  */
2247 int
BMenu_count_items(void * menu)2248 BMenu_count_items (void *menu)
2249 {
2250   return ((BMenu *) menu)->CountItems ();
2251 }
2252 
2253 /* Find the item in MENU at IDX.  */
2254 void *
BMenu_item_at(void * menu,int idx)2255 BMenu_item_at (void *menu, int idx)
2256 {
2257   return ((BMenu *) menu)->ItemAt (idx);
2258 }
2259 
2260 /* Set ITEM's label to LABEL.  */
2261 void
BMenu_item_set_label(void * item,const char * label)2262 BMenu_item_set_label (void *item, const char *label)
2263 {
2264   ((BMenuItem *) item)->SetLabel (label);
2265 }
2266 
2267 /* Get ITEM's menu.  */
2268 void *
BMenu_item_get_menu(void * item)2269 BMenu_item_get_menu (void *item)
2270 {
2271   return ((BMenuItem *) item)->Submenu ();
2272 }
2273 
2274 /* Emit a beep noise.  */
2275 void
haiku_ring_bell(void)2276 haiku_ring_bell (void)
2277 {
2278   beep ();
2279 }
2280 
2281 /* Create a BAlert with TEXT.  */
2282 void *
BAlert_new(const char * text,enum haiku_alert_type type)2283 BAlert_new (const char *text, enum haiku_alert_type type)
2284 {
2285   return new BAlert (NULL, text, NULL, NULL, NULL, B_WIDTH_AS_USUAL,
2286 		     (enum alert_type) type);
2287 }
2288 
2289 /* Add a button to ALERT and return the button.  */
2290 void *
BAlert_add_button(void * alert,const char * text)2291 BAlert_add_button (void *alert, const char *text)
2292 {
2293   BAlert *al = (BAlert *) alert;
2294   al->AddButton (text);
2295   return al->ButtonAt (al->CountButtons () - 1);
2296 }
2297 
2298 /* Run ALERT, returning the number of the button that was selected,
2299    or -1 if no button was selected before the alert was closed.  */
2300 int32_t
BAlert_go(void * alert)2301 BAlert_go (void *alert)
2302 {
2303   return ((BAlert *) alert)->Go ();
2304 }
2305 
2306 /* Enable or disable BUTTON depending on ENABLED_P.  */
2307 void
BButton_set_enabled(void * button,int enabled_p)2308 BButton_set_enabled (void *button, int enabled_p)
2309 {
2310   ((BButton *) button)->SetEnabled (enabled_p);
2311 }
2312 
2313 /* Set VIEW's tooltip to TOOLTIP.  */
2314 void
BView_set_tooltip(void * view,const char * tooltip)2315 BView_set_tooltip (void *view, const char *tooltip)
2316 {
2317   ((BView *) view)->SetToolTip (tooltip);
2318 }
2319 
2320 /* Set VIEW's tooltip to a sticky tooltip at X by Y.  */
2321 void
BView_set_and_show_sticky_tooltip(void * view,const char * tooltip,int x,int y)2322 BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
2323 				   int x, int y)
2324 {
2325   BToolTip *tip;
2326   BView *vw = (BView *) view;
2327   if (!vw->LockLooper ())
2328     gui_abort ("Failed to lock view while showing sticky tooltip");
2329   vw->SetToolTip (tooltip);
2330   tip = vw->ToolTip ();
2331   BPoint pt;
2332   EmacsView *ev = dynamic_cast<EmacsView *> (vw);
2333   if (ev)
2334     ev->tt_absl_pos = BPoint (x, y);
2335 
2336   vw->GetMouse (&pt, NULL, 1);
2337   pt.x -= x;
2338   pt.y -= y;
2339 
2340   pt.x = -pt.x;
2341   pt.y = -pt.y;
2342 
2343   tip->SetMouseRelativeLocation (pt);
2344   tip->SetSticky (1);
2345   vw->ShowToolTip (tip);
2346   vw->UnlockLooper ();
2347 }
2348 
2349 /* Delete ALERT.  */
2350 void
BAlert_delete(void * alert)2351 BAlert_delete (void *alert)
2352 {
2353   delete (BAlert *) alert;
2354 }
2355 
2356 /* Place the resolution of the monitor in DPI in RSSX and RSSY.  */
2357 void
BScreen_res(double * rrsx,double * rrsy)2358 BScreen_res (double *rrsx, double *rrsy)
2359 {
2360   BScreen s (B_MAIN_SCREEN_ID);
2361   if (!s.IsValid ())
2362     gui_abort ("Invalid screen for resolution checks");
2363   monitor_info i;
2364 
2365   if (s.GetMonitorInfo (&i) == B_OK)
2366     {
2367       *rrsx = (double) i.width / (double) 2.54;
2368       *rrsy = (double) i.height / (double) 2.54;
2369     }
2370   else
2371     {
2372       *rrsx = 72.27;
2373       *rrsy = 72.27;
2374     }
2375 }
2376 
2377 /* Add WINDOW to OTHER_WINDOW's subset and parent it to
2378    OTHER_WINDOW.  */
2379 void
EmacsWindow_parent_to(void * window,void * other_window)2380 EmacsWindow_parent_to (void *window, void *other_window)
2381 {
2382   EmacsWindow *w = (EmacsWindow *) window;
2383   if (!w->LockLooper ())
2384     gui_abort ("Failed to lock window while parenting");
2385   w->ParentTo ((EmacsWindow *) other_window);
2386   w->UnlockLooper ();
2387 }
2388 
2389 void
EmacsWindow_unparent(void * window)2390 EmacsWindow_unparent (void *window)
2391 {
2392   EmacsWindow *w = (EmacsWindow *) window;
2393   if (!w->LockLooper ())
2394     gui_abort ("Failed to lock window while unparenting");
2395   w->UnparentAndUnlink ();
2396   w->UnlockLooper ();
2397 }
2398 
2399 /* Place text describing the current version of Haiku in VERSION,
2400    which should be a buffer LEN bytes wide.  */
2401 void
be_get_version_string(char * version,int len)2402 be_get_version_string (char *version, int len)
2403 {
2404   std::strncpy (version, "Unknown Haiku release", len - 1);
2405   BPath path;
2406   if (find_directory (B_BEOS_LIB_DIRECTORY, &path) == B_OK)
2407     {
2408       path.Append ("libbe.so");
2409 
2410       BAppFileInfo appFileInfo;
2411       version_info versionInfo;
2412       BFile file;
2413       if (file.SetTo (path.Path (), B_READ_ONLY) == B_OK
2414           && appFileInfo.SetTo (&file) == B_OK
2415           && appFileInfo.GetVersionInfo (&versionInfo,
2416                                          B_APP_VERSION_KIND) == B_OK
2417           && versionInfo.short_info[0] != '\0')
2418 	std::strncpy (version, versionInfo.short_info, len - 1);
2419     }
2420 }
2421 
2422 /* Return the amount of color planes in the current display.  */
2423 int
be_get_display_planes(void)2424 be_get_display_planes (void)
2425 {
2426   color_space space = dpy_color_space;
2427   if (space == B_NO_COLOR_SPACE)
2428     {
2429       BScreen screen; /* This is actually a very slow operation.  */
2430       if (!screen.IsValid ())
2431 	gui_abort ("Invalid screen");
2432       space = dpy_color_space = screen.ColorSpace ();
2433     }
2434 
2435   if (space == B_RGB32 || space == B_RGB24)
2436     return 24;
2437   if (space == B_RGB16)
2438     return 16;
2439   if (space == B_RGB15)
2440     return 15;
2441   if (space == B_CMAP8)
2442     return 8;
2443 
2444   gui_abort ("Bad colorspace for screen");
2445   /* https://www.haiku-os.org/docs/api/classBScreen.html
2446      says a valid screen can't be anything else.  */
2447   return -1;
2448 }
2449 
2450 /* Return the amount of colors the display can handle.  */
2451 int
be_get_display_color_cells(void)2452 be_get_display_color_cells (void)
2453 {
2454   color_space space = dpy_color_space;
2455   if (space == B_NO_COLOR_SPACE)
2456     {
2457       BScreen screen;
2458       if (!screen.IsValid ())
2459 	gui_abort ("Invalid screen");
2460       space = dpy_color_space = screen.ColorSpace ();
2461     }
2462 
2463   if (space == B_RGB32 || space == B_RGB24)
2464     return 1677216;
2465   if (space == B_RGB16)
2466     return 65536;
2467   if (space == B_RGB15)
2468     return 32768;
2469   if (space == B_CMAP8)
2470     return 256;
2471 
2472   gui_abort ("Bad colorspace for screen");
2473   return -1;
2474 }
2475 
2476 /* Warp the pointer to X by Y.  */
2477 void
be_warp_pointer(int x,int y)2478 be_warp_pointer (int x, int y)
2479 {
2480   /* We're not supposed to use the following function without a
2481      BWindowScreen object, but in Haiku nothing actually prevents us
2482      from doing so.  */
2483 
2484   set_mouse_position (x, y);
2485 }
2486 
2487 /* Update the position of CHILD in WINDOW without actually moving
2488    it.  */
2489 void
EmacsWindow_move_weak_child(void * window,void * child,int xoff,int yoff)2490 EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff)
2491 {
2492   EmacsWindow *w = (EmacsWindow *) window;
2493   EmacsWindow *c = (EmacsWindow *) child;
2494 
2495   if (!w->LockLooper ())
2496     gui_abort ("Couldn't lock window for weak move");
2497   w->MoveChild (c, xoff, yoff, 1);
2498   w->UnlockLooper ();
2499 }
2500 
2501 /* Find an appropriate view to draw onto.  If VW is double-buffered,
2502    this will be the view used for double buffering instead of VW
2503    itself.  */
2504 void *
find_appropriate_view_for_draw(void * vw)2505 find_appropriate_view_for_draw (void *vw)
2506 {
2507   BView *v = (BView *) vw;
2508   EmacsView *ev = dynamic_cast<EmacsView *>(v);
2509   if (!ev)
2510     return v;
2511 
2512   return ev->offscreen_draw_view ? ev->offscreen_draw_view : vw;
2513 }
2514 
2515 /* Set up double buffering for VW.  */
2516 void
EmacsView_set_up_double_buffering(void * vw)2517 EmacsView_set_up_double_buffering (void *vw)
2518 {
2519   EmacsView *view = (EmacsView *) vw;
2520   if (!view->LockLooper ())
2521     gui_abort ("Couldn't lock view while setting up double buffering");
2522   if (view->offscreen_draw_view)
2523     {
2524       view->UnlockLooper ();
2525       return;
2526     }
2527   view->SetUpDoubleBuffering ();
2528   view->UnlockLooper ();
2529 }
2530 
2531 /* Flip and invalidate the view VW.  */
2532 void
EmacsView_flip_and_blit(void * vw)2533 EmacsView_flip_and_blit (void *vw)
2534 {
2535   EmacsView *view = (EmacsView *) vw;
2536   if (!view->offscreen_draw_view)
2537     return;
2538   if (!view->LockLooper ())
2539     gui_abort ("Couldn't lock view in flip_and_blit");
2540   view->FlipBuffers ();
2541   view->UnlockLooper ();
2542 }
2543 
2544 /* Disable double buffering for VW.  */
2545 void
EmacsView_disable_double_buffering(void * vw)2546 EmacsView_disable_double_buffering (void *vw)
2547 {
2548   EmacsView *view = (EmacsView *) vw;
2549   if (!view->LockLooper ())
2550     gui_abort ("Couldn't lock view tearing down double buffering");
2551   view->TearDownDoubleBuffering ();
2552   view->UnlockLooper ();
2553 }
2554 
2555 /* Return non-0 if VW is double-buffered.  */
2556 int
EmacsView_double_buffered_p(void * vw)2557 EmacsView_double_buffered_p (void *vw)
2558 {
2559   EmacsView *view = (EmacsView *) vw;
2560   if (!view->LockLooper ())
2561     gui_abort ("Couldn't lock view testing double buffering status");
2562   int db_p = !!view->offscreen_draw_view;
2563   view->UnlockLooper ();
2564   return db_p;
2565 }
2566 
2567 struct popup_file_dialog_data
2568 {
2569   BMessage *msg;
2570   BFilePanel *panel;
2571   BEntry *entry;
2572 };
2573 
2574 static void
unwind_popup_file_dialog(void * ptr)2575 unwind_popup_file_dialog (void *ptr)
2576 {
2577   struct popup_file_dialog_data *data =
2578     (struct popup_file_dialog_data *) ptr;
2579   BFilePanel *panel = data->panel;
2580   delete panel;
2581   delete data->entry;
2582   delete data->msg;
2583 }
2584 
2585 static void
be_popup_file_dialog_safe_set_target(BFilePanel * dialog,BWindow * window)2586 be_popup_file_dialog_safe_set_target (BFilePanel *dialog, BWindow *window)
2587 {
2588   dialog->SetTarget (BMessenger (window));
2589 }
2590 
2591 /* Popup a file dialog.  */
2592 char *
be_popup_file_dialog(int open_p,const char * default_dir,int must_match_p,int dir_only_p,void * window,const char * save_text,const char * prompt,void (* block_input_function)(void),void (* unblock_input_function)(void))2593 be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int dir_only_p,
2594 		      void *window, const char *save_text, const char *prompt,
2595 		      void (*block_input_function) (void),
2596 		      void (*unblock_input_function) (void))
2597 {
2598   ptrdiff_t idx = c_specpdl_idx_from_cxx ();
2599   /* setjmp/longjmp is UB with automatic objects. */
2600   block_input_function ();
2601   BWindow *w = (BWindow *) window;
2602   uint32_t mode = dir_only_p ? B_DIRECTORY_NODE : B_FILE_NODE | B_DIRECTORY_NODE;
2603   BEntry *path = new BEntry;
2604   BMessage *msg = new BMessage ('FPSE');
2605   BFilePanel *panel = new BFilePanel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL,
2606 				      NULL, NULL, mode);
2607   unblock_input_function ();
2608 
2609   struct popup_file_dialog_data dat;
2610   dat.entry = path;
2611   dat.msg = msg;
2612   dat.panel = panel;
2613 
2614   record_c_unwind_protect_from_cxx (unwind_popup_file_dialog, &dat);
2615   if (default_dir)
2616     {
2617       if (path->SetTo (default_dir, 0) != B_OK)
2618 	default_dir = NULL;
2619     }
2620 
2621   panel->SetMessage (msg);
2622   if (default_dir)
2623     panel->SetPanelDirectory (path);
2624   if (save_text)
2625     panel->SetSaveText (save_text);
2626   panel->SetHideWhenDone (0);
2627   panel->Window ()->SetTitle (prompt);
2628   be_popup_file_dialog_safe_set_target (panel, w);
2629 
2630   panel->Show ();
2631   panel->Window ()->Show ();
2632 
2633   void *buf = alloca (200);
2634   while (1)
2635     {
2636       enum haiku_event_type type;
2637       char *ptr = NULL;
2638 
2639       if (!haiku_read_with_timeout (&type, buf, 200, 100000))
2640 	{
2641 	  if (type != FILE_PANEL_EVENT)
2642 	    haiku_write (type, buf);
2643 	  else if (!ptr)
2644 	    ptr = (char *) ((struct haiku_file_panel_event *) buf)->ptr;
2645 	}
2646 
2647       ssize_t b_s;
2648       haiku_read_size (&b_s);
2649       if (!b_s || b_s == -1 || ptr || panel->Window ()->IsHidden ())
2650 	{
2651 	  c_unbind_to_nil_from_cxx (idx);
2652 	  return ptr;
2653 	}
2654     }
2655 }
2656 
2657 void
be_app_quit(void)2658 be_app_quit (void)
2659 {
2660   if (be_app)
2661     {
2662       while (!be_app->Lock ());
2663       be_app->Quit ();
2664     }
2665 }
2666 
2667 /* Temporarily fill VIEW with COLOR.  */
2668 void
EmacsView_do_visible_bell(void * view,uint32_t color)2669 EmacsView_do_visible_bell (void *view, uint32_t color)
2670 {
2671   EmacsView *vw = (EmacsView *) view;
2672   vw->DoVisibleBell (color);
2673 }
2674 
2675 /* Zoom WINDOW.  */
2676 void
BWindow_zoom(void * window)2677 BWindow_zoom (void *window)
2678 {
2679   BWindow *w = (BWindow *) window;
2680   w->Zoom ();
2681 }
2682 
2683 /* Make WINDOW fullscreen if FULLSCREEN_P.  */
2684 void
EmacsWindow_make_fullscreen(void * window,int fullscreen_p)2685 EmacsWindow_make_fullscreen (void *window, int fullscreen_p)
2686 {
2687   EmacsWindow *w = (EmacsWindow *) window;
2688   w->MakeFullscreen (fullscreen_p);
2689 }
2690 
2691 /* Unzoom (maximize) WINDOW.  */
2692 void
EmacsWindow_unzoom(void * window)2693 EmacsWindow_unzoom (void *window)
2694 {
2695   EmacsWindow *w = (EmacsWindow *) window;
2696   w->UnZoom ();
2697 }
2698 
2699 /* Move the pointer into MBAR and start tracking.  */
2700 void
BMenuBar_start_tracking(void * mbar)2701 BMenuBar_start_tracking (void *mbar)
2702 {
2703   EmacsMenuBar *mb = (EmacsMenuBar *) mbar;
2704   if (!mb->LockLooper ())
2705     gui_abort ("Couldn't lock menubar");
2706   BRect frame = mb->Frame ();
2707   BPoint pt = frame.LeftTop ();
2708   BPoint l = pt;
2709   mb->Parent ()->ConvertToScreen (&pt);
2710   set_mouse_position (pt.x, pt.y);
2711   mb->MouseDown (l);
2712   mb->UnlockLooper ();
2713 }
2714 
2715 #ifdef HAVE_NATIVE_IMAGE_API
2716 int
be_can_translate_type_to_bitmap_p(const char * mime)2717 be_can_translate_type_to_bitmap_p (const char *mime)
2718 {
2719   BTranslatorRoster *r = BTranslatorRoster::Default ();
2720   translator_id *ids;
2721   int32 id_len;
2722 
2723   if (r->GetAllTranslators (&ids, &id_len) != B_OK)
2724     return 0;
2725 
2726   int found_in = 0;
2727   int found_out = 0;
2728 
2729   for (int i = 0; i < id_len; ++i)
2730     {
2731       found_in = 0;
2732       found_out = 0;
2733       const translation_format *i_fmts;
2734       const translation_format *o_fmts;
2735 
2736       int32 i_count, o_count;
2737 
2738       if (r->GetInputFormats (ids[i], &i_fmts, &i_count) != B_OK)
2739 	continue;
2740 
2741       if (r->GetOutputFormats (ids[i], &o_fmts, &o_count) != B_OK)
2742 	continue;
2743 
2744       for (int x = 0; x < i_count; ++x)
2745 	{
2746 	  if (!strcmp (i_fmts[x].MIME, mime))
2747 	    {
2748 	      found_in = 1;
2749 	      break;
2750 	    }
2751 	}
2752 
2753       for (int x = 0; x < i_count; ++x)
2754 	{
2755 	  if (!strcmp (o_fmts[x].MIME, "image/x-be-bitmap") ||
2756 	      !strcmp (o_fmts[x].MIME, "image/x-vnd.Be-bitmap"))
2757 	    {
2758 	      found_out = 1;
2759 	      break;
2760 	    }
2761 	}
2762 
2763       if (found_in && found_out)
2764 	break;
2765     }
2766 
2767   delete [] ids;
2768 
2769   return found_in && found_out;
2770 }
2771 
2772 void *
be_translate_bitmap_from_file_name(const char * filename)2773 be_translate_bitmap_from_file_name (const char *filename)
2774 {
2775   BBitmap *bm = BTranslationUtils::GetBitmap (filename);
2776   return bm;
2777 }
2778 
2779 void *
be_translate_bitmap_from_memory(const void * buf,size_t bytes)2780 be_translate_bitmap_from_memory (const void *buf, size_t bytes)
2781 {
2782   BMemoryIO io (buf, bytes);
2783   BBitmap *bm = BTranslationUtils::GetBitmap (&io);
2784   return bm;
2785 }
2786 #endif
2787 
2788 /* Return the size of BITMAP's data, in bytes.  */
2789 size_t
BBitmap_bytes_length(void * bitmap)2790 BBitmap_bytes_length (void *bitmap)
2791 {
2792   BBitmap *bm = (BBitmap *) bitmap;
2793   return bm->BitsLength ();
2794 }
2795 
2796 /* Show VIEW's tooltip.  */
2797 void
BView_show_tooltip(void * view)2798 BView_show_tooltip (void *view)
2799 {
2800   BView *vw = (BView *) view;
2801   if (vw->LockLooper ())
2802     {
2803       vw->ShowToolTip (vw->ToolTip ());
2804       vw->UnlockLooper ();
2805     }
2806 }
2807 
2808 
2809 #ifdef USE_BE_CAIRO
2810 /* Return VIEW's cairo surface.  */
2811 cairo_surface_t *
EmacsView_cairo_surface(void * view)2812 EmacsView_cairo_surface (void *view)
2813 {
2814   EmacsView *vw = (EmacsView *) view;
2815   return vw->cr_surface;
2816 }
2817 
2818 /* Transfer each clip rectangle in VIEW to the cairo context
2819    CTX.  */
2820 void
BView_cr_dump_clipping(void * view,cairo_t * ctx)2821 BView_cr_dump_clipping (void *view, cairo_t *ctx)
2822 {
2823   BView *vw = (BView *) find_appropriate_view_for_draw (view);
2824   BRegion cr;
2825   vw->GetClippingRegion (&cr);
2826 
2827   for (int i = 0; i < cr.CountRects (); ++i)
2828     {
2829       BRect r = cr.RectAt (i);
2830       cairo_rectangle (ctx, r.left, r.top, r.Width () + 1,
2831 		       r.Height () + 1);
2832     }
2833 
2834   cairo_clip (ctx);
2835 }
2836 
2837 /* Lock WINDOW in preparation for drawing using Cairo.  */
2838 void
EmacsWindow_begin_cr_critical_section(void * window)2839 EmacsWindow_begin_cr_critical_section (void *window)
2840 {
2841   BWindow *w = (BWindow *) window;
2842   BView *vw = (BView *) w->FindView ("Emacs");
2843   EmacsView *ev = dynamic_cast <EmacsView *> (vw);
2844   if (ev && !ev->cr_surface_lock.Lock ())
2845     gui_abort ("Couldn't lock view cairo surface");
2846 }
2847 
2848 /* Unlock WINDOW in preparation for drawing using Cairo.  */
2849 void
EmacsWindow_end_cr_critical_section(void * window)2850 EmacsWindow_end_cr_critical_section (void *window)
2851 {
2852   BWindow *w = (BWindow *) window;
2853   BView *vw = (BView *) w->FindView ("Emacs");
2854   EmacsView *ev = dynamic_cast <EmacsView *> (vw);
2855   if (ev)
2856     ev->cr_surface_lock.Unlock ();
2857 }
2858 #endif
2859 
2860 /* Get the width of STR in the plain font.  */
2861 int
be_string_width_with_plain_font(const char * str)2862 be_string_width_with_plain_font (const char *str)
2863 {
2864   return be_plain_font->StringWidth (str);
2865 }
2866 
2867 /* Get the ascent + descent of the plain font.  */
2868 int
be_plain_font_height(void)2869 be_plain_font_height (void)
2870 {
2871   struct font_height fheight;
2872   be_plain_font->GetHeight (&fheight);
2873 
2874   return fheight.ascent + fheight.descent;
2875 }
2876 
2877 /* Return the number of physical displays connected.  */
2878 int
be_get_display_screens(void)2879 be_get_display_screens (void)
2880 {
2881   int count = 1;
2882   BScreen scr;
2883 
2884   if (!scr.IsValid ())
2885     gui_abort ("Main screen vanished!");
2886   while (scr.SetToNext () == B_OK && scr.IsValid ())
2887     ++count;
2888 
2889   return count;
2890 }
2891 
2892 /* Set the minimum width the user can resize WINDOW to.  */
2893 void
BWindow_set_min_size(void * window,int width,int height)2894 BWindow_set_min_size (void *window, int width, int height)
2895 {
2896   BWindow *w = (BWindow *) window;
2897 
2898   if (!w->LockLooper ())
2899     gui_abort ("Failed to lock window looper setting min size");
2900   w->SetSizeLimits (width, -1, height, -1);
2901   w->UnlockLooper ();
2902 }
2903 
2904 /* Synchronize WINDOW's connection to the App Server.  */
2905 void
BWindow_sync(void * window)2906 BWindow_sync (void *window)
2907 {
2908   BWindow *w = (BWindow *) window;
2909 
2910   if (!w->LockLooper ())
2911     gui_abort ("Failed to lock window looper for sync");
2912   w->Sync ();
2913   w->UnlockLooper ();
2914 }
2915 
2916 /* Set the alignment of WINDOW's dimensions.  */
2917 void
BWindow_set_size_alignment(void * window,int align_width,int align_height)2918 BWindow_set_size_alignment (void *window, int align_width, int align_height)
2919 {
2920   BWindow *w = (BWindow *) window;
2921 
2922   if (!w->LockLooper ())
2923     gui_abort ("Failed to lock window looper setting alignment");
2924 #if 0 /* Haiku does not currently implement SetWindowAlignment.  */
2925   if (w->SetWindowAlignment (B_PIXEL_ALIGNMENT, -1, -1, align_width,
2926 			     align_width, -1, -1, align_height,
2927 			     align_height) != B_NO_ERROR)
2928     gui_abort ("Invalid pixel alignment");
2929 #endif
2930   w->UnlockLooper ();
2931 }
2932