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