root/src/haiku_support.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gui_abort
  2. be_popup_menu_thread_entry
  3. keysym_from_raw_char
  4. map_key
  5. map_shift
  6. map_caps
  7. map_caps_shift
  8. map_normal
  9. get_zoom_rect
  10. DispatchMessage
  11. my_team_id
  12. settings_valid_p
  13. AboutRequested
  14. QuitRequested
  15. MessageReceived
  16. ArgvReceived
  17. fullscreen_mode
  18. RecomputeFeel
  19. UpwardsSubset
  20. UpwardsSubsetChildren
  21. UpwardsUnSubset
  22. UpwardsUnSubsetChildren
  23. Unparent
  24. UnparentAndUnlink
  25. UnlinkChild
  26. ParentTo
  27. LinkChild
  28. MoveToIncludingFrame
  29. DoMove
  30. DoUpdateWorkspace
  31. MoveChild
  32. WindowActivated
  33. MessageReceived
  34. DispatchMessage
  35. MenusBeginning
  36. MenusEnded
  37. FrameResized
  38. FrameMoved
  39. WorkspacesChanged
  40. EmacsMoveTo
  41. QuitRequested
  42. Minimize
  43. EmacsHide
  44. EmacsShow
  45. ClearFullscreen
  46. FullscreenRectForMode
  47. SetFullscreen
  48. Zoom
  49. OffsetChildRect
  50. AttachedToWindow
  51. FrameResized
  52. MouseDown
  53. MouseMoved
  54. MessageReceived
  55. use_frame_synchronization
  56. SetFrameSynchronization
  57. MessageReceived
  58. DetachCairoSurface
  59. AttachCairoSurface
  60. TearDownDoubleBuffering
  61. AfterResize
  62. Draw
  63. FlipBuffers
  64. SetUpDoubleBuffering
  65. MouseMoved
  66. BasicMouseDown
  67. MouseDown
  68. BasicMouseUp
  69. MouseUp
  70. parent
  71. MessageReceived
  72. Pulse
  73. ValueChanged
  74. ButtonRegionFor
  75. MouseDown
  76. MouseUp
  77. MouseMoved
  78. DrawContent
  79. help
  80. DrawContent
  81. GetContentSize
  82. Highlight
  83. DoLayout
  84. QuitRequested
  85. MessageReceived
  86. current_font
  87. FrameResized
  88. MinSize
  89. view_3
  90. ShowPreview
  91. UpdatePreview
  92. HidePreview
  93. UpdateStylesForIndex
  94. QuitRequested
  95. UpdateForSelectedStyle
  96. MessageReceived
  97. preview
  98. FrameResized
  99. WaitForChoice
  100. InitCheck
  101. MessageReceived
  102. ReadFileName
  103. InitCheck
  104. AttachedToWindow
  105. start_running_application
  106. BBitmap_data
  107. BBitmap_convert
  108. BBitmap_free
  109. BBitmap_new
  110. BBitmap_dimensions
  111. wait_for_exit_of_app_thread
  112. BApplication_setup
  113. BWindow_new
  114. BWindow_quit
  115. BWindow_set_offset
  116. BWindow_dimensions
  117. BWindow_iconify
  118. BWindow_set_visible
  119. BWindow_retitle
  120. BWindow_resize
  121. BWindow_activate
  122. be_get_screen_dimensions
  123. BView_resize_to
  124. be_delete_cursor
  125. be_create_cursor_from_id
  126. BView_set_view_cursor
  127. BWindow_Flush
  128. be_make_scroll_bar_for_view
  129. BScrollBar_delete
  130. BView_move_frame
  131. BView_scroll_bar_update
  132. BScrollBar_default_size
  133. BView_invalidate
  134. BView_draw_lock
  135. BView_invalidate_region
  136. BView_draw_unlock
  137. BWindow_center_on_screen
  138. BBitmap_import_fringe_bitmap
  139. BView_publish_scroll_bar
  140. BView_forget_scroll_bar
  141. BView_inside_scroll_bar
  142. BView_get_mouse
  143. BView_convert_to_screen
  144. BView_convert_from_screen
  145. BWindow_change_decoration
  146. BWindow_set_tooltip_decoration
  147. BWindow_set_avoid_focus
  148. BView_emacs_delete
  149. BPopUpMenu_new
  150. BMenu_add_title
  151. BMenu_add_item
  152. BMenu_add_separator
  153. BMenu_new_submenu
  154. BMenu_new_menu_bar_submenu
  155. BMenu_run
  156. BPopUpMenu_delete
  157. BMenuBar_new
  158. BMenuBar_delete
  159. BMenu_delete_all
  160. BMenu_delete_from
  161. BMenu_count_items
  162. BMenu_item_at
  163. BMenu_item_set_label
  164. BMenu_item_get_menu
  165. haiku_ring_bell
  166. BAlert_new
  167. BAlert_add_button
  168. BAlert_set_offset_spacing
  169. be_alert_thread_entry
  170. BAlert_go
  171. BButton_set_enabled
  172. BView_set_tooltip
  173. be_show_sticky_tooltip
  174. BAlert_delete
  175. be_get_display_resolution
  176. EmacsWindow_parent_to
  177. EmacsWindow_unparent
  178. be_get_version_string
  179. be_get_display_planes
  180. be_get_display_color_cells
  181. be_is_display_grayscale
  182. be_warp_pointer
  183. EmacsWindow_move_weak_child
  184. find_appropriate_view_for_draw
  185. EmacsView_set_up_double_buffering
  186. EmacsView_flip_and_blit
  187. EmacsView_disable_double_buffering
  188. EmacsView_double_buffered_p
  189. be_popup_file_dialog
  190. BMenuBar_start_tracking
  191. be_can_translate_type_to_bitmap_p
  192. be_translate_bitmap_from_file_name
  193. be_translate_bitmap_from_memory
  194. BBitmap_bytes_length
  195. BView_show_tooltip
  196. EmacsView_cairo_context
  197. BView_cr_dump_clipping
  198. EmacsWindow_begin_cr_critical_section
  199. EmacsWindow_end_cr_critical_section
  200. be_string_width_with_plain_font
  201. be_plain_font_height
  202. be_get_display_screens
  203. BWindow_sync
  204. BWindow_set_size_alignment
  205. BWindow_send_behind
  206. BWindow_is_active
  207. be_use_subpixel_antialiasing
  208. BWindow_set_override_redirect
  209. be_find_setting
  210. BMessage_delete
  211. be_drag_message_thread_entry
  212. be_drag_message
  213. be_drag_and_drop_in_progress
  214. be_replay_menu_bar_event
  215. BWindow_set_z_group
  216. be_get_ui_color
  217. be_select_font
  218. BWindow_set_sticky
  219. be_roster_launch
  220. be_create_pixmap_cursor
  221. be_get_window_decorator_dimensions
  222. be_get_window_decorator_frame
  223. be_send_move_frame_event
  224. be_lock_window
  225. be_unlock_window
  226. be_set_window_fullscreen_mode
  227. be_get_explicit_workarea
  228. be_clear_grab_view
  229. be_set_use_frame_synchronization
  230. be_write_node_message
  231. be_send_message

     1 /* Haiku window system support.  Hey, Emacs, this is -*- C++ -*-
     2    Copyright (C) 2021-2023 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 #include <attribute.h>
    21 
    22 #include <app/Application.h>
    23 #include <app/Cursor.h>
    24 #include <app/Clipboard.h>
    25 #include <app/Messenger.h>
    26 #include <app/Roster.h>
    27 
    28 #include <interface/GraphicsDefs.h>
    29 #include <interface/InterfaceDefs.h>
    30 #include <interface/Bitmap.h>
    31 #include <interface/Window.h>
    32 #include <interface/View.h>
    33 #include <interface/Screen.h>
    34 #include <interface/ScrollBar.h>
    35 #include <interface/Region.h>
    36 #include <interface/Menu.h>
    37 #include <interface/MenuItem.h>
    38 #include <interface/PopUpMenu.h>
    39 #include <interface/MenuBar.h>
    40 #include <interface/Alert.h>
    41 #include <interface/Button.h>
    42 #include <interface/ControlLook.h>
    43 #include <interface/Deskbar.h>
    44 #include <interface/ListView.h>
    45 #include <interface/StringItem.h>
    46 #include <interface/SplitView.h>
    47 #include <interface/ScrollView.h>
    48 #include <interface/StringView.h>
    49 #include <interface/TextControl.h>
    50 #include <interface/CheckBox.h>
    51 
    52 #include <locale/UnicodeChar.h>
    53 
    54 #include <game/WindowScreen.h>
    55 #include <game/DirectWindow.h>
    56 
    57 #include <storage/FindDirectory.h>
    58 #include <storage/Entry.h>
    59 #include <storage/Path.h>
    60 #include <storage/FilePanel.h>
    61 #include <storage/AppFileInfo.h>
    62 #include <storage/Path.h>
    63 #include <storage/PathFinder.h>
    64 #include <storage/Node.h>
    65 
    66 #include <support/Beep.h>
    67 #include <support/DataIO.h>
    68 #include <support/Locker.h>
    69 #include <support/ObjectList.h>
    70 
    71 #include <translation/TranslatorRoster.h>
    72 #include <translation/TranslationDefs.h>
    73 #include <translation/TranslationUtils.h>
    74 
    75 #include <kernel/OS.h>
    76 #include <kernel/fs_attr.h>
    77 #include <kernel/scheduler.h>
    78 
    79 #include <private/interface/ToolTip.h>
    80 #include <private/interface/WindowPrivate.h>
    81 
    82 #include <cmath>
    83 #include <cstring>
    84 #include <cstdint>
    85 #include <cstdio>
    86 #include <csignal>
    87 #include <cfloat>
    88 
    89 #ifdef USE_BE_CAIRO
    90 #include <cairo.h>
    91 #endif
    92 
    93 #include "haiku_support.h"
    94 
    95 /* Some messages that Emacs sends to itself.  */
    96 enum
    97   {
    98     SCROLL_BAR_UPDATE        = 3000,
    99     WAIT_FOR_RELEASE         = 3001,
   100     RELEASE_NOW              = 3002,
   101     CANCEL_DROP              = 3003,
   102     SHOW_MENU_BAR            = 3004,
   103     BE_MENU_BAR_OPEN         = 3005,
   104     QUIT_APPLICATION         = 3006,
   105     REPLAY_MENU_BAR          = 3007,
   106     FONT_FAMILY_SELECTED     = 3008,
   107     FONT_STYLE_SELECTED      = 3009,
   108     FILE_PANEL_SELECTION     = 3010,
   109     QUIT_PREVIEW_DIALOG      = 3011,
   110     SET_FONT_INDICES         = 3012,
   111     SET_PREVIEW_DIALOG       = 3013,
   112     UPDATE_PREVIEW_DIALOG    = 3014,
   113     SEND_MOVE_FRAME_EVENT    = 3015,
   114     SET_DISABLE_ANTIALIASING = 3016,
   115   };
   116 
   117 /* X11 keysyms that we use.  */
   118 enum
   119   {
   120     KEY_BACKSPACE         = 0xff08,
   121     KEY_TAB               = 0xff09,
   122     KEY_RETURN            = 0xff0d,
   123     KEY_PAUSE             = 0xff13,
   124     KEY_ESCAPE            = 0xff1b,
   125     KEY_DELETE            = 0xffff,
   126     KEY_HOME              = 0xff50,
   127     KEY_LEFT_ARROW        = 0xff51,
   128     KEY_UP_ARROW          = 0xff52,
   129     KEY_RIGHT_ARROW       = 0xff53,
   130     KEY_DOWN_ARROW        = 0xff54,
   131     KEY_PAGE_UP           = 0xff55,
   132     KEY_PAGE_DOWN         = 0xff56,
   133     KEY_END               = 0xff57,
   134     KEY_PRINT             = 0xff61,
   135     KEY_INSERT            = 0xff63,
   136     /* This is used to indicate the first function key.  */
   137     KEY_F1                = 0xffbe,
   138     /* These are found on some multilingual keyboards.  */
   139     KEY_HANGUL            = 0xff31,
   140     KEY_HANGUL_HANJA      = 0xff34,
   141     KEY_HIRIGANA_KATAGANA = 0xff27,
   142     KEY_ZENKAKU_HANKAKU   = 0xff2a,
   143   };
   144 
   145 struct font_selection_dialog_message
   146 {
   147   /* Whether or not font selection was canceled.  */
   148   bool_bf cancel : 1;
   149 
   150   /* Whether or not a size was explicitly specified.  */
   151   bool_bf size_specified : 1;
   152 
   153   /* Whether or not antialiasing should be disabled.  */
   154   bool_bf disable_antialias : 1;
   155 
   156   /* The index of the selected font family.  */
   157   int family_idx;
   158 
   159   /* The index of the selected font style.  */
   160   int style_idx;
   161 
   162   /* The selected font size.  */
   163   int size;
   164 };
   165 
   166 /* The color space of the main screen.  B_NO_COLOR_SPACE means it has
   167    not yet been computed.  */
   168 static color_space dpy_color_space = B_NO_COLOR_SPACE;
   169 
   170 /* The keymap, or NULL if it has not been initialized.  */
   171 static key_map *key_map;
   172 
   173 /* Indices of characters into the keymap.  */
   174 static char *key_chars;
   175 
   176 /* Lock around keymap data, since it's touched from different
   177    threads.  */
   178 static BLocker key_map_lock;
   179 
   180 /* The locking semantics of BWindows running in multiple threads are
   181    so complex that child frame state (which is the only state that is
   182    shared between different BWindows at runtime) does best with a
   183    single global lock.  */
   184 static BLocker child_frame_lock;
   185 
   186 /* Variable where the popup menu thread returns the chosen menu
   187    item.  */
   188 static BMessage volatile *popup_track_message;
   189 
   190 /* Variable in which alert dialog threads return the selected button
   191    number.  */
   192 static int32 volatile alert_popup_value;
   193 
   194 /* The view that has the passive grab.  */
   195 static void *grab_view;
   196 
   197 /* The locker for that variable.  */
   198 static BLocker grab_view_locker;
   199 
   200 /* Whether or not a drag-and-drop operation is in progress.  */
   201 static bool drag_and_drop_in_progress;
   202 
   203 /* Many places require us to lock the child frame data, and then lock
   204    the locker of some random window.  Unfortunately, locking such a
   205    window might be delayed due to an arriving message, which then
   206    calls a callback inside that window that tries to lock the child
   207    frame data but doesn't finish since the child frame lock is already
   208    held, not letting the code that held the child frame lock proceed,
   209    thereby causing a deadlock.
   210 
   211    Rectifying that problem is simple: all code in a looper callback
   212    must lock the child frame data with this macro instead.
   213 
   214    IOW, if some other code is already running with the child frame
   215    lock held, don't interfere: wait until it's finished before
   216    continuing.  */
   217 #define CHILD_FRAME_LOCK_INSIDE_LOOPER_CALLBACK         \
   218   if (child_frame_lock.LockWithTimeout (200) != B_OK)   \
   219     {                                                   \
   220       /* The Haiku equivalent of XPutBackEvent.  */     \
   221       if (CurrentMessage ())                            \
   222         PostMessage (CurrentMessage ());                \
   223     }                                                   \
   224   else
   225 
   226 /* This could be a private API, but it's used by (at least) the Qt
   227    port, so it's probably here to stay.  */
   228 extern status_t get_subpixel_antialiasing (bool *);
   229 
   230 /* The ID of the thread the BApplication is running in.  */
   231 static thread_id app_thread;
   232 
   233 _Noreturn void
   234 gui_abort (const char *msg)
   235 {
   236   fprintf (stderr, "Abort in GUI code: %s\n", msg);
   237   fprintf (stderr, "Under Haiku, Emacs cannot recover from errors in GUI code\n");
   238   fprintf (stderr, "App Server disconnects usually manifest as bitmap "
   239            "initialization failures or lock failures.");
   240   abort ();
   241 }
   242 
   243 struct be_popup_menu_data
   244 {
   245   int x, y;
   246   BPopUpMenu *menu;
   247 };
   248 
   249 static int32
   250 be_popup_menu_thread_entry (void *thread_data)
   251 {
   252   struct be_popup_menu_data *data;
   253   struct haiku_dummy_event dummy;
   254   BMenuItem *it;
   255 
   256   data = (struct be_popup_menu_data *) thread_data;
   257 
   258   it = data->menu->Go (BPoint (data->x, data->y));
   259 
   260   if (it)
   261     popup_track_message = it->Message ();
   262   else
   263     popup_track_message = NULL;
   264 
   265   haiku_write (DUMMY_EVENT, &dummy);
   266   return 0;
   267 }
   268 
   269 /* Convert a raw character RAW produced by the keycode KEY into a key
   270    symbol and place it in KEYSYM.
   271 
   272    If RAW cannot be converted into a keysym, value is 0.  If RAW can
   273    be converted into a keysym, but it should be ignored, value is -1.
   274 
   275    Any other value means success, and that the keysym should be used
   276    instead of mapping the keycode into a character.  */
   277 
   278 static int
   279 keysym_from_raw_char (int32 raw, int32 key, unsigned *code)
   280 {
   281   switch (raw)
   282     {
   283     case B_BACKSPACE:
   284       *code = KEY_BACKSPACE;
   285       break;
   286     case B_RETURN:
   287       *code = KEY_RETURN;
   288       break;
   289     case B_TAB:
   290       *code = KEY_TAB;
   291       break;
   292     case B_ESCAPE:
   293       *code = KEY_ESCAPE;
   294       break;
   295     case B_LEFT_ARROW:
   296       *code = KEY_LEFT_ARROW;
   297       break;
   298     case B_RIGHT_ARROW:
   299       *code = KEY_RIGHT_ARROW;
   300       break;
   301     case B_UP_ARROW:
   302       *code = KEY_UP_ARROW;
   303       break;
   304     case B_DOWN_ARROW:
   305       *code = KEY_DOWN_ARROW;
   306       break;
   307     case B_INSERT:
   308       *code = KEY_INSERT;
   309       break;
   310     case B_DELETE:
   311       *code = KEY_DELETE;
   312       break;
   313     case B_HOME:
   314       *code = KEY_HOME;
   315       break;
   316     case B_END:
   317       *code = KEY_END;
   318       break;
   319     case B_PAGE_UP:
   320       *code = KEY_PAGE_UP;
   321       break;
   322     case B_PAGE_DOWN:
   323       *code = KEY_PAGE_DOWN;
   324       break;
   325 
   326     case B_FUNCTION_KEY:
   327       *code = KEY_F1 + key - 2;
   328 
   329       if (*code - KEY_F1 == 12)
   330         *code = KEY_PRINT;
   331       else if (*code - KEY_F1 == 13)
   332         /* Okay, Scroll Lock is a bit too much: keyboard.c doesn't
   333            know about it yet, and it shouldn't, since that's a
   334            modifier key.
   335 
   336            *code = KEY_SCROLL_LOCK; */
   337         return -1;
   338       else if (*code - KEY_F1 == 14)
   339         *code = KEY_PAUSE;
   340 
   341       break;
   342 
   343     case B_HANGUL:
   344       *code = KEY_HANGUL;
   345       break;
   346     case B_HANGUL_HANJA:
   347       *code = KEY_HANGUL_HANJA;
   348       break;
   349     case B_KATAKANA_HIRAGANA:
   350       *code = KEY_HIRIGANA_KATAGANA;
   351       break;
   352     case B_HANKAKU_ZENKAKU:
   353       *code = KEY_ZENKAKU_HANKAKU;
   354       break;
   355 
   356     default:
   357       return 0;
   358     }
   359 
   360   return 1;
   361 }
   362 
   363 static void
   364 map_key (char *chars, int32 offset, uint32_t *c)
   365 {
   366   int size = chars[offset++];
   367   switch (size)
   368     {
   369     case 0:
   370       break;
   371 
   372     case 1:
   373       *c = chars[offset];
   374       break;
   375 
   376     default:
   377       {
   378         char str[5];
   379         int i = (size <= 4) ? size : 4;
   380         strncpy (str, &(chars[offset]), i);
   381         str[i] = '0';
   382         *c = BUnicodeChar::FromUTF8 ((char *) &str);
   383         break;
   384       }
   385     }
   386 }
   387 
   388 static void
   389 map_shift (uint32_t kc, uint32_t *ch)
   390 {
   391   if (!key_map_lock.Lock ())
   392     gui_abort ("Failed to lock keymap");
   393   if (!key_map)
   394     get_key_map (&key_map, &key_chars);
   395   if (!key_map)
   396     return;
   397   if (kc >= 128)
   398     return;
   399 
   400   int32_t m = key_map->shift_map[kc];
   401   map_key (key_chars, m, ch);
   402   key_map_lock.Unlock ();
   403 }
   404 
   405 static void
   406 map_caps (uint32_t kc, uint32_t *ch)
   407 {
   408   if (!key_map_lock.Lock ())
   409     gui_abort ("Failed to lock keymap");
   410   if (!key_map)
   411     get_key_map (&key_map, &key_chars);
   412   if (!key_map)
   413     return;
   414   if (kc >= 128)
   415     return;
   416 
   417   int32_t m = key_map->caps_map[kc];
   418   map_key (key_chars, m, ch);
   419   key_map_lock.Unlock ();
   420 }
   421 
   422 static void
   423 map_caps_shift (uint32_t kc, uint32_t *ch)
   424 {
   425   if (!key_map_lock.Lock ())
   426     gui_abort ("Failed to lock keymap");
   427   if (!key_map)
   428     get_key_map (&key_map, &key_chars);
   429   if (!key_map)
   430     return;
   431   if (kc >= 128)
   432     return;
   433 
   434   int32_t m = key_map->caps_shift_map[kc];
   435   map_key (key_chars, m, ch);
   436   key_map_lock.Unlock ();
   437 }
   438 
   439 static void
   440 map_normal (uint32_t kc, uint32_t *ch)
   441 {
   442   if (!key_map_lock.Lock ())
   443     gui_abort ("Failed to lock keymap");
   444   if (!key_map)
   445     get_key_map (&key_map, &key_chars);
   446   if (!key_map)
   447     return;
   448   if (kc >= 128)
   449     return;
   450 
   451   int32_t m = key_map->normal_map[kc];
   452   map_key (key_chars, m, ch);
   453   key_map_lock.Unlock ();
   454 }
   455 
   456 static BRect
   457 get_zoom_rect (BWindow *window)
   458 {
   459   BScreen screen;
   460   BDeskbar deskbar;
   461   BRect screen_frame;
   462   BRect frame;
   463   BRect deskbar_frame;
   464   BRect window_frame;
   465   BRect decorator_frame;
   466 
   467   if (!screen.IsValid ())
   468     gui_abort ("Failed to calculate screen rect");
   469 
   470   screen_frame = frame = screen.Frame ();
   471   deskbar_frame = deskbar.Frame ();
   472 
   473   if (!(modifiers () & B_SHIFT_KEY) && !deskbar.IsAutoHide ())
   474     {
   475       switch (deskbar.Location ())
   476         {
   477         case B_DESKBAR_TOP:
   478           frame.top = deskbar_frame.bottom + 2;
   479           break;
   480 
   481         case B_DESKBAR_BOTTOM:
   482         case B_DESKBAR_LEFT_BOTTOM:
   483         case B_DESKBAR_RIGHT_BOTTOM:
   484           frame.bottom = deskbar_frame.top - 2;
   485           break;
   486 
   487         case B_DESKBAR_LEFT_TOP:
   488           if (!deskbar.IsExpanded ())
   489             frame.top = deskbar_frame.bottom + 2;
   490           else if (!deskbar.IsAlwaysOnTop ()
   491                    && !deskbar.IsAutoRaise ())
   492             frame.left = deskbar_frame.right + 2;
   493           break;
   494 
   495         default:
   496           if (deskbar.IsExpanded ()
   497               && !deskbar.IsAlwaysOnTop ()
   498               && !deskbar.IsAutoRaise ())
   499             frame.right = deskbar_frame.left - 2;
   500         }
   501     }
   502 
   503   if (window)
   504     {
   505       window_frame = window->Frame ();
   506       decorator_frame = window->DecoratorFrame ();
   507 
   508       frame.top += (window_frame.top
   509                     - decorator_frame.top);
   510       frame.bottom -= (decorator_frame.bottom
   511                        - window_frame.bottom);
   512       frame.left += (window_frame.left
   513                      - decorator_frame.left);
   514       frame.right -= (decorator_frame.right
   515                       - window_frame.right);
   516 
   517       if (frame.top > deskbar_frame.bottom
   518           || frame.bottom < deskbar_frame.top)
   519         {
   520           frame.left = screen_frame.left + (window_frame.left
   521                                             - decorator_frame.left);
   522           frame.right = screen_frame.right - (decorator_frame.right
   523                                               - window_frame.right);
   524         }
   525     }
   526 
   527   return frame;
   528 }
   529 
   530 /* Invisible window used to get B_SCREEN_CHANGED events.  */
   531 class EmacsScreenChangeMonitor : public BWindow
   532 {
   533   BRect previous_screen_frame;
   534 
   535 public:
   536   EmacsScreenChangeMonitor (void) : BWindow (BRect (-100, -100, 0, 0), "",
   537                                              B_NO_BORDER_WINDOW_LOOK,
   538                                              B_FLOATING_ALL_WINDOW_FEEL,
   539                                              B_AVOID_FRONT | B_AVOID_FOCUS)
   540   {
   541     BScreen screen (this);
   542 
   543     if (!screen.IsValid ())
   544       return;
   545 
   546     previous_screen_frame = screen.Frame ();
   547 
   548     /* Immediately show this window upon creation.  It will not steal
   549        the focus or become visible.  */
   550     Show ();
   551 
   552     if (!LockLooper ())
   553       return;
   554 
   555     Hide ();
   556     UnlockLooper ();
   557   }
   558 
   559   void
   560   DispatchMessage (BMessage *msg, BHandler *handler)
   561   {
   562     struct haiku_screen_changed_event rq;
   563     BRect frame;
   564 
   565     if (msg->what == B_SCREEN_CHANGED)
   566       {
   567         if (msg->FindInt64 ("when", &rq.when) != B_OK)
   568           rq.when = 0;
   569 
   570         if (msg->FindRect ("frame", &frame) != B_OK
   571             || frame != previous_screen_frame)
   572           {
   573             haiku_write (SCREEN_CHANGED_EVENT, &rq);
   574 
   575             if (frame.IsValid ())
   576               previous_screen_frame = frame;
   577           }
   578       }
   579 
   580     BWindow::DispatchMessage (msg, handler);
   581   }
   582 };
   583 
   584 #if 0
   585 
   586 /* Return the ID of this team.  */
   587 
   588 static team_id
   589 my_team_id (void)
   590 {
   591   thread_id id;
   592   thread_info info;
   593 
   594   id = find_thread (NULL);
   595   get_thread_info (id, &info);
   596 
   597   return info.team;
   598 }
   599 
   600 #endif /* 0 */
   601 
   602 class Emacs : public BApplication
   603 {
   604 public:
   605   BMessage settings;
   606   bool settings_valid_p;
   607   EmacsScreenChangeMonitor *monitor;
   608 
   609   Emacs (void) : BApplication ("application/x-vnd.GNU-emacs"),
   610                  settings_valid_p (false)
   611   {
   612     BPath settings_path;
   613 
   614     if (find_directory (B_USER_SETTINGS_DIRECTORY, &settings_path) != B_OK)
   615       return;
   616 
   617     settings_path.Append (PACKAGE_NAME);
   618 
   619     BEntry entry (settings_path.Path ());
   620     BFile settings_file (&entry, B_READ_ONLY | B_CREATE_FILE);
   621 
   622     if (settings.Unflatten (&settings_file) != B_OK)
   623       return;
   624 
   625     settings_valid_p = true;
   626     monitor = new EmacsScreenChangeMonitor;
   627   }
   628 
   629   ~Emacs (void)
   630   {
   631     if (monitor->LockLooper ())
   632       monitor->Quit ();
   633     else
   634       delete monitor;
   635   }
   636 
   637   void
   638   AboutRequested (void)
   639   {
   640     BAlert *about = new BAlert (PACKAGE_NAME,
   641                                 PACKAGE_STRING
   642                                 "\nThe extensible, self-documenting, "
   643                                 "real-time display editor.",
   644                                 "Close");
   645     about->Go ();
   646   }
   647 
   648   bool
   649   QuitRequested (void)
   650   {
   651     struct haiku_app_quit_requested_event rq;
   652     struct haiku_session_manager_reply reply;
   653     int32 reply_type;
   654 
   655     haiku_write (APP_QUIT_REQUESTED_EVENT, &rq);
   656 
   657     if (read_port (port_emacs_to_session_manager,
   658                    &reply_type, &reply, sizeof reply) < B_OK)
   659       /* Return true so the system kills us, since there's no real
   660          alternative if this read fails.  */
   661       return true;
   662 
   663     return reply.quit_reply;
   664   }
   665 
   666   void
   667   MessageReceived (BMessage *msg)
   668   {
   669     struct haiku_clipboard_changed_event rq;
   670 
   671     if (msg->what == QUIT_APPLICATION)
   672       Quit ();
   673     else if (msg->what == B_CLIPBOARD_CHANGED)
   674       haiku_write (CLIPBOARD_CHANGED_EVENT, &rq);
   675     else if (msg->what == B_KEY_MAP_LOADED)
   676       {
   677         /* Install the new keymap.  Or rather, clear key_map -- Emacs
   678            will fetch it again from the main thread the next time it
   679            is needed.  */
   680         if (key_map_lock.Lock ())
   681           {
   682             if (key_map)
   683               free (key_map);
   684 
   685             if (key_chars)
   686               free (key_chars);
   687 
   688             key_map = NULL;
   689             key_chars = NULL;
   690             key_map_lock.Unlock ();
   691           }
   692       }
   693     else
   694       BApplication::MessageReceived (msg);
   695   }
   696 
   697   /* The code below doesn't function; see `be_display_notification'
   698      for further specifics.  */
   699 
   700 #if 0
   701   void
   702   ArgvReceived (int32 argc, char **argv)
   703   {
   704     struct haiku_notification_click_event rq;
   705     intmax_t id;
   706     team_id team;
   707 
   708     /* ArgvReceived is called after Emacs is first started, with each
   709        command line argument passed to Emacs.  It is, moreover, called
   710        with ARGC set to 1 and ARGV[0] a string starting with
   711        -Notification, after a notification is clicked.  This string
   712        both incorporates the team ID and the notification ID.  */
   713 
   714     if (argc == 1
   715         && sscanf (argv[0], "-Notification,%d.%jd", &team, &id) == 2)
   716       {
   717         /* Since this is a valid notification message, generate an
   718            event if the team ID matches.  */
   719         if (team == my_team_id ())
   720           {
   721             rq.id = id;
   722             haiku_write (NOTIFICATION_CLICK_EVENT, &rq);
   723           }
   724       }
   725 
   726     BApplication::ArgvReceived (argc, argv);
   727   }
   728 #endif /* 0 */
   729 };
   730 
   731 class EmacsWindow : public BWindow
   732 {
   733 public:
   734   struct child_frame
   735   {
   736     struct child_frame *next;
   737     int xoff, yoff;
   738     EmacsWindow *window;
   739   } *subset_windows;
   740 
   741   EmacsWindow *parent;
   742   BRect pre_fullscreen_rect;
   743   BRect pre_zoom_rect;
   744   int x_before_zoom;
   745   int y_before_zoom;
   746   bool shown_flag;
   747   volatile bool was_shown_p;
   748   bool menu_bar_active_p;
   749   bool override_redirect_p;
   750   window_look pre_override_redirect_look;
   751   window_feel pre_override_redirect_feel;
   752   uint32 pre_override_redirect_workspaces;
   753   int window_id;
   754   bool *menus_begun;
   755   enum haiku_z_group z_group;
   756   bool tooltip_p;
   757   enum haiku_fullscreen_mode fullscreen_mode;
   758 
   759   EmacsWindow () : BWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK,
   760                             B_NORMAL_WINDOW_FEEL, B_NO_SERVER_SIDE_WINDOW_MODIFIERS),
   761                    subset_windows (NULL),
   762                    parent (NULL),
   763                    x_before_zoom (INT_MIN),
   764                    y_before_zoom (INT_MIN),
   765                    shown_flag (false),
   766                    was_shown_p (false),
   767                    menu_bar_active_p (false),
   768                    override_redirect_p (false),
   769                    menus_begun (NULL),
   770                    z_group (Z_GROUP_NONE),
   771                    tooltip_p (false),
   772                    fullscreen_mode (FULLSCREEN_MODE_NONE)
   773   {
   774     /* This pulse rate is used by scroll bars for repeating a button
   775        action while a button is held down.  */
   776     SetPulseRate (30000);
   777   }
   778 
   779   ~EmacsWindow ()
   780   {
   781     if (!child_frame_lock.Lock ())
   782       gui_abort ("Failed to lock child frame state lock");
   783     struct child_frame *next;
   784     for (struct child_frame *f = subset_windows; f; f = next)
   785       {
   786         if (f->window->LockLooper ())
   787           gui_abort ("Failed to lock looper for unparent");
   788         f->window->Unparent ();
   789         f->window->UnlockLooper ();
   790         next = f->next;
   791         delete f;
   792       }
   793 
   794     if (this->parent)
   795       UnparentAndUnlink ();
   796     child_frame_lock.Unlock ();
   797   }
   798 
   799   void
   800   RecomputeFeel (void)
   801   {
   802     if (override_redirect_p || tooltip_p)
   803       SetFeel (kMenuWindowFeel);
   804     else if (parent)
   805       SetFeel (B_FLOATING_SUBSET_WINDOW_FEEL);
   806     else if (z_group == Z_GROUP_ABOVE)
   807       SetFeel (B_FLOATING_ALL_WINDOW_FEEL);
   808     else
   809       SetFeel (B_NORMAL_WINDOW_FEEL);
   810   }
   811 
   812   void
   813   UpwardsSubset (EmacsWindow *w)
   814   {
   815     for (; w; w = w->parent)
   816       AddToSubset (w);
   817   }
   818 
   819   void
   820   UpwardsSubsetChildren (EmacsWindow *w)
   821   {
   822     if (!LockLooper ())
   823       gui_abort ("Failed to lock looper for subset");
   824     if (!child_frame_lock.Lock ())
   825       gui_abort ("Failed to lock child frame state lock");
   826     UpwardsSubset (w);
   827     for (struct child_frame *f = subset_windows; f;
   828          f = f->next)
   829       f->window->UpwardsSubsetChildren (w);
   830     child_frame_lock.Unlock ();
   831     UnlockLooper ();
   832   }
   833 
   834   void
   835   UpwardsUnSubset (EmacsWindow *w)
   836   {
   837     for (; w; w = w->parent)
   838       RemoveFromSubset (w);
   839   }
   840 
   841   void
   842   UpwardsUnSubsetChildren (EmacsWindow *w)
   843   {
   844     if (!LockLooper ())
   845       gui_abort ("Failed to lock looper for unsubset");
   846     if (!child_frame_lock.Lock ())
   847       gui_abort ("Failed to lock child frame state lock");
   848     UpwardsUnSubset (w);
   849     for (struct child_frame *f = subset_windows; f;
   850          f = f->next)
   851       f->window->UpwardsUnSubsetChildren (w);
   852     child_frame_lock.Unlock ();
   853     UnlockLooper ();
   854   }
   855 
   856   void
   857   Unparent (void)
   858   {
   859     EmacsWindow *parent;
   860 
   861     if (!child_frame_lock.Lock ())
   862       gui_abort ("Failed to lock child frame state lock");
   863 
   864     parent = this->parent;
   865     this->parent = NULL;
   866     RecomputeFeel ();
   867     UpwardsUnSubsetChildren (parent);
   868     this->RemoveFromSubset (this);
   869     child_frame_lock.Unlock ();
   870   }
   871 
   872   void
   873   UnparentAndUnlink (void)
   874   {
   875     if (!child_frame_lock.Lock ())
   876       gui_abort ("Failed to lock child frame state lock");
   877     this->parent->UnlinkChild (this);
   878     this->Unparent ();
   879     child_frame_lock.Unlock ();
   880   }
   881 
   882   void
   883   UnlinkChild (EmacsWindow *window)
   884   {
   885     struct child_frame *last = NULL;
   886     struct child_frame *tem = subset_windows;
   887 
   888     for (; tem; last = tem, tem = tem->next)
   889       {
   890         if (tem->window == window)
   891           {
   892             if (last)
   893               last->next = tem->next;
   894             else
   895               subset_windows = tem->next;
   896             delete tem;
   897             return;
   898           }
   899       }
   900 
   901     gui_abort ("Failed to unlink child frame");
   902   }
   903 
   904   void
   905   ParentTo (EmacsWindow *window)
   906   {
   907     if (!child_frame_lock.Lock ())
   908       gui_abort ("Failed to lock child frame state lock");
   909 
   910     if (this->parent)
   911       UnparentAndUnlink ();
   912 
   913     this->parent = window;
   914     RecomputeFeel ();
   915     this->AddToSubset (this);
   916     if (!IsHidden () && this->parent)
   917       UpwardsSubsetChildren (parent);
   918     window->LinkChild (this);
   919 
   920     child_frame_lock.Unlock ();
   921   }
   922 
   923   void
   924   LinkChild (EmacsWindow *window)
   925   {
   926     struct child_frame *f = new struct child_frame;
   927 
   928     for (struct child_frame *f = subset_windows; f;
   929          f = f->next)
   930       {
   931         if (window == f->window)
   932           gui_abort ("Trying to link a child frame that is already present");
   933       }
   934 
   935     f->window = window;
   936     f->next = subset_windows;
   937     f->xoff = -1;
   938     f->yoff = -1;
   939 
   940     subset_windows = f;
   941   }
   942 
   943   void
   944   MoveToIncludingFrame (int x, int y)
   945   {
   946     BRect decorator, frame;
   947 
   948     decorator = DecoratorFrame ();
   949     frame = Frame ();
   950 
   951     MoveTo (x + frame.left - decorator.left,
   952             y + frame.top - decorator.top);
   953   }
   954 
   955   void
   956   DoMove (struct child_frame *f)
   957   {
   958     BRect frame = this->Frame ();
   959     f->window->MoveToIncludingFrame (frame.left + f->xoff,
   960                                      frame.top + f->yoff);
   961   }
   962 
   963   void
   964   DoUpdateWorkspace (struct child_frame *f)
   965   {
   966     f->window->SetWorkspaces (this->Workspaces ());
   967   }
   968 
   969   void
   970   MoveChild (EmacsWindow *window, int xoff, int yoff,
   971              int weak_p)
   972   {
   973     if (!child_frame_lock.Lock ())
   974       gui_abort ("Failed to lock child frame state lock");
   975 
   976     for (struct child_frame *f = subset_windows; f;
   977          f = f->next)
   978       {
   979         if (window == f->window)
   980           {
   981             f->xoff = xoff;
   982             f->yoff = yoff;
   983             if (!weak_p)
   984               DoMove (f);
   985 
   986             child_frame_lock.Unlock ();
   987             return;
   988           }
   989       }
   990 
   991     child_frame_lock.Unlock ();
   992     gui_abort ("Trying to move a child frame that doesn't exist");
   993   }
   994 
   995   void
   996   WindowActivated (bool activated)
   997   {
   998     struct haiku_activation_event rq;
   999     rq.window = this;
  1000     rq.activated_p = activated;
  1001 
  1002     haiku_write (ACTIVATION, &rq);
  1003   }
  1004 
  1005   void
  1006   MessageReceived (BMessage *msg)
  1007   {
  1008     if (msg->WasDropped ())
  1009       {
  1010         BPoint whereto;
  1011         int64 threadid;
  1012         struct haiku_drag_and_drop_event rq;
  1013 
  1014         if (msg->FindInt64 ("emacs:thread_id", &threadid) == B_OK
  1015             && threadid == find_thread (NULL))
  1016           return;
  1017 
  1018         whereto = msg->DropPoint ();
  1019 
  1020         this->ConvertFromScreen (&whereto);
  1021 
  1022         rq.window = this;
  1023         rq.message = DetachCurrentMessage ();
  1024         rq.x = whereto.x;
  1025         rq.y = whereto.y;
  1026 
  1027         haiku_write (DRAG_AND_DROP_EVENT, &rq);
  1028       }
  1029     else if (msg->GetPointer ("menuptr"))
  1030       {
  1031         struct haiku_menu_bar_select_event rq;
  1032 
  1033         rq.window = this;
  1034         rq.ptr = (void *) msg->GetPointer ("menuptr");
  1035 
  1036         haiku_write (MENU_BAR_SELECT_EVENT, &rq);
  1037       }
  1038     else
  1039       BWindow::MessageReceived (msg);
  1040   }
  1041 
  1042   void
  1043   DispatchMessage (BMessage *msg, BHandler *handler)
  1044   {
  1045     if (msg->what == B_KEY_DOWN || msg->what == B_KEY_UP)
  1046       {
  1047         struct haiku_key_event rq;
  1048 
  1049         /* Pass through key events to the regular dispatch mechanism
  1050            if the menu bar active, so that key navigation can work.  */
  1051         if (menu_bar_active_p)
  1052           {
  1053             BWindow::DispatchMessage (msg, handler);
  1054             return;
  1055           }
  1056 
  1057         rq.window = this;
  1058 
  1059         int32 raw, key;
  1060         int ret;
  1061         msg->FindInt32 ("raw_char", &raw);
  1062         msg->FindInt32 ("key", &key);
  1063         msg->FindInt64 ("when", &rq.time);
  1064 
  1065         rq.modifiers = 0;
  1066         uint32_t mods = modifiers ();
  1067 
  1068         if (mods & B_SHIFT_KEY)
  1069           rq.modifiers |= HAIKU_MODIFIER_SHIFT;
  1070 
  1071         if (mods & B_CONTROL_KEY)
  1072           rq.modifiers |= HAIKU_MODIFIER_CTRL;
  1073 
  1074         if (mods & B_COMMAND_KEY)
  1075           rq.modifiers |= HAIKU_MODIFIER_ALT;
  1076 
  1077         if (mods & B_OPTION_KEY)
  1078           rq.modifiers |= HAIKU_MODIFIER_SUPER;
  1079 
  1080         ret = keysym_from_raw_char (raw, key, &rq.keysym);
  1081 
  1082         if (!ret)
  1083           rq.keysym = 0;
  1084 
  1085         if (ret < 0)
  1086           return;
  1087 
  1088         rq.multibyte_char = 0;
  1089 
  1090         if (!rq.keysym)
  1091           {
  1092             if (mods & B_SHIFT_KEY)
  1093               {
  1094                 if (mods & B_CAPS_LOCK)
  1095                   map_caps_shift (key, &rq.multibyte_char);
  1096                 else
  1097                   map_shift (key, &rq.multibyte_char);
  1098               }
  1099             else
  1100               {
  1101                 if (mods & B_CAPS_LOCK)
  1102                   map_caps (key, &rq.multibyte_char);
  1103                 else
  1104                   map_normal (key, &rq.multibyte_char);
  1105               }
  1106           }
  1107 
  1108         haiku_write (msg->what == B_KEY_DOWN ? KEY_DOWN : KEY_UP, &rq);
  1109       }
  1110     else if (msg->what == B_MOUSE_WHEEL_CHANGED)
  1111       {
  1112         struct haiku_wheel_move_event rq;
  1113         rq.window = this;
  1114         rq.modifiers = 0;
  1115 
  1116         uint32_t mods = modifiers ();
  1117 
  1118         if (mods & B_SHIFT_KEY)
  1119           rq.modifiers |= HAIKU_MODIFIER_SHIFT;
  1120 
  1121         if (mods & B_CONTROL_KEY)
  1122           rq.modifiers |= HAIKU_MODIFIER_CTRL;
  1123 
  1124         if (mods & B_COMMAND_KEY)
  1125           rq.modifiers |= HAIKU_MODIFIER_ALT;
  1126 
  1127         if (mods & B_OPTION_KEY)
  1128           rq.modifiers |= HAIKU_MODIFIER_SUPER;
  1129 
  1130         float dx, dy;
  1131         if (msg->FindFloat ("be:wheel_delta_x", &dx) == B_OK &&
  1132             msg->FindFloat ("be:wheel_delta_y", &dy) == B_OK)
  1133           {
  1134             rq.delta_x = dx;
  1135             rq.delta_y = dy;
  1136 
  1137             haiku_write (WHEEL_MOVE_EVENT, &rq);
  1138           };
  1139       }
  1140     else if (msg->what == SEND_MOVE_FRAME_EVENT)
  1141       FrameMoved (Frame ().LeftTop ());
  1142     else if (msg->what == B_SCREEN_CHANGED)
  1143       {
  1144         if (fullscreen_mode != FULLSCREEN_MODE_NONE)
  1145           SetFullscreen (fullscreen_mode);
  1146 
  1147         BWindow::DispatchMessage (msg, handler);
  1148       }
  1149     else
  1150       BWindow::DispatchMessage (msg, handler);
  1151   }
  1152 
  1153   void
  1154   MenusBeginning (void)
  1155   {
  1156     struct haiku_menu_bar_state_event rq;
  1157 
  1158     rq.window = this;
  1159     if (!menus_begun)
  1160       haiku_write (MENU_BAR_OPEN, &rq);
  1161     else
  1162       *menus_begun = true;
  1163 
  1164     menu_bar_active_p = true;
  1165   }
  1166 
  1167   void
  1168   MenusEnded ()
  1169   {
  1170     struct haiku_menu_bar_state_event rq;
  1171     rq.window = this;
  1172 
  1173     haiku_write (MENU_BAR_CLOSE, &rq);
  1174     menu_bar_active_p = false;
  1175   }
  1176 
  1177   void
  1178   FrameResized (float newWidth, float newHeight)
  1179   {
  1180     struct haiku_resize_event rq;
  1181     rq.window = this;
  1182     rq.width = newWidth + 1.0f;
  1183     rq.height = newHeight + 1.0f;
  1184 
  1185     haiku_write (FRAME_RESIZED, &rq);
  1186     BWindow::FrameResized (newWidth, newHeight);
  1187   }
  1188 
  1189   void
  1190   FrameMoved (BPoint new_position)
  1191   {
  1192     struct haiku_move_event rq;
  1193     BRect frame, decorator_frame;
  1194     struct child_frame *f;
  1195 
  1196     if (fullscreen_mode == FULLSCREEN_MODE_WIDTH
  1197         && new_position.x != 0)
  1198       {
  1199         MoveTo (0, new_position.y);
  1200         return;
  1201       }
  1202 
  1203     if (fullscreen_mode == FULLSCREEN_MODE_HEIGHT
  1204         && new_position.y != 0)
  1205       {
  1206         MoveTo (new_position.x, 0);
  1207         return;
  1208       }
  1209 
  1210     rq.window = this;
  1211     rq.x = std::lrint (new_position.x);
  1212     rq.y = std::lrint (new_position.y);
  1213 
  1214     frame = Frame ();
  1215     decorator_frame = DecoratorFrame ();
  1216 
  1217     rq.decorator_width
  1218       = std::lrint (frame.left - decorator_frame.left);
  1219     rq.decorator_height
  1220       = std::lrint (frame.top - decorator_frame.top);
  1221 
  1222     haiku_write (MOVE_EVENT, &rq);
  1223 
  1224     CHILD_FRAME_LOCK_INSIDE_LOOPER_CALLBACK
  1225       {
  1226         for (f = subset_windows; f; f = f->next)
  1227           DoMove (f);
  1228         child_frame_lock.Unlock ();
  1229 
  1230         BWindow::FrameMoved (new_position);
  1231       }
  1232   }
  1233 
  1234   void
  1235   WorkspacesChanged (uint32_t old, uint32_t n)
  1236   {
  1237     struct child_frame *f;
  1238 
  1239     CHILD_FRAME_LOCK_INSIDE_LOOPER_CALLBACK
  1240       {
  1241         for (f = subset_windows; f; f = f->next)
  1242           DoUpdateWorkspace (f);
  1243 
  1244         child_frame_lock.Unlock ();
  1245       }
  1246   }
  1247 
  1248   void
  1249   EmacsMoveTo (int x, int y)
  1250   {
  1251     if (!child_frame_lock.Lock ())
  1252       gui_abort ("Failed to lock child frame state lock");
  1253 
  1254     if (!this->parent)
  1255       this->MoveToIncludingFrame (x, y);
  1256     else
  1257       this->parent->MoveChild (this, x, y, 0);
  1258     child_frame_lock.Unlock ();
  1259   }
  1260 
  1261   bool
  1262   QuitRequested ()
  1263   {
  1264     struct haiku_quit_requested_event rq;
  1265     rq.window = this;
  1266     haiku_write (QUIT_REQUESTED, &rq);
  1267     return false;
  1268   }
  1269 
  1270   void
  1271   Minimize (bool minimized_p)
  1272   {
  1273     struct haiku_iconification_event rq;
  1274 
  1275     rq.window = this;
  1276     rq.iconified_p = !parent && minimized_p;
  1277     haiku_write (ICONIFICATION, &rq);
  1278 
  1279     BWindow::Minimize (minimized_p);
  1280   }
  1281 
  1282   void
  1283   EmacsHide (void)
  1284   {
  1285     if (this->IsHidden ())
  1286       return;
  1287     if (!child_frame_lock.Lock ())
  1288       gui_abort ("Failed to lock child frame state lock");
  1289 
  1290     Hide ();
  1291     if (this->parent)
  1292       UpwardsUnSubsetChildren (this->parent);
  1293 
  1294     child_frame_lock.Unlock ();
  1295   }
  1296 
  1297   void
  1298   EmacsShow (void)
  1299   {
  1300     if (!this->IsHidden ())
  1301       return;
  1302 
  1303     if (!child_frame_lock.Lock ())
  1304       gui_abort ("Failed to lock child frame state lock");
  1305 
  1306     if (!was_shown_p)
  1307       {
  1308         /* This window is being shown for the first time, which means
  1309            Show will unlock the looper.  In this case, it should be
  1310            locked again, since the looper is unlocked when the window
  1311            is first created.  */
  1312 
  1313         if (!LockLooper ())
  1314           gui_abort ("Failed to lock looper during first window show");
  1315         was_shown_p = true;
  1316       }
  1317 
  1318     if (this->parent)
  1319       shown_flag = 1;
  1320     Show ();
  1321     if (this->parent)
  1322       UpwardsSubsetChildren (this->parent);
  1323 
  1324     child_frame_lock.Unlock ();
  1325   }
  1326 
  1327   BRect
  1328   ClearFullscreen (enum haiku_fullscreen_mode target_mode)
  1329   {
  1330     BRect original_frame;
  1331 
  1332     switch (fullscreen_mode)
  1333       {
  1334       case FULLSCREEN_MODE_MAXIMIZED:
  1335         original_frame = pre_zoom_rect;
  1336 
  1337         if (target_mode == FULLSCREEN_MODE_NONE)
  1338           BWindow::Zoom (pre_zoom_rect.LeftTop (),
  1339                          BE_RECT_WIDTH (pre_zoom_rect) - 1,
  1340                          BE_RECT_HEIGHT (pre_zoom_rect) - 1);
  1341         break;
  1342 
  1343       case FULLSCREEN_MODE_BOTH:
  1344       case FULLSCREEN_MODE_HEIGHT:
  1345       case FULLSCREEN_MODE_WIDTH:
  1346         original_frame = pre_fullscreen_rect;
  1347         SetFlags (Flags () & ~(B_NOT_MOVABLE
  1348                                | B_NOT_ZOOMABLE
  1349                                | B_NOT_RESIZABLE));
  1350 
  1351         if (target_mode != FULLSCREEN_MODE_NONE)
  1352           goto out;
  1353 
  1354         MoveTo (pre_fullscreen_rect.LeftTop ());
  1355         ResizeTo (BE_RECT_WIDTH (pre_fullscreen_rect) - 1,
  1356                   BE_RECT_HEIGHT (pre_fullscreen_rect) - 1);
  1357         break;
  1358 
  1359       case FULLSCREEN_MODE_NONE:
  1360         original_frame = Frame ();
  1361         break;
  1362       }
  1363 
  1364   out:
  1365     fullscreen_mode = FULLSCREEN_MODE_NONE;
  1366     return original_frame;
  1367   }
  1368 
  1369   BRect
  1370   FullscreenRectForMode (enum haiku_fullscreen_mode mode)
  1371   {
  1372     BScreen screen (this);
  1373     BRect frame;
  1374 
  1375     if (!screen.IsValid ())
  1376       return BRect (0, 0, 0, 0);
  1377 
  1378     frame = screen.Frame ();
  1379 
  1380     if (mode == FULLSCREEN_MODE_HEIGHT)
  1381       frame.right -= BE_RECT_WIDTH (frame) / 2;
  1382     else if (mode == FULLSCREEN_MODE_WIDTH)
  1383       frame.bottom -= BE_RECT_HEIGHT (frame) / 2;
  1384 
  1385     return frame;
  1386   }
  1387 
  1388   void
  1389   SetFullscreen (enum haiku_fullscreen_mode mode)
  1390   {
  1391     BRect zoom_rect, frame;
  1392 
  1393     frame = ClearFullscreen (mode);
  1394 
  1395     switch (mode)
  1396       {
  1397       case FULLSCREEN_MODE_MAXIMIZED:
  1398         pre_zoom_rect = frame;
  1399         zoom_rect = get_zoom_rect (this);
  1400         BWindow::Zoom (zoom_rect.LeftTop (),
  1401                        BE_RECT_WIDTH (zoom_rect) - 1,
  1402                        BE_RECT_HEIGHT (zoom_rect) - 1);
  1403         break;
  1404 
  1405       case FULLSCREEN_MODE_BOTH:
  1406         SetFlags (Flags () | B_NOT_MOVABLE);
  1407         FALLTHROUGH;
  1408 
  1409       case FULLSCREEN_MODE_HEIGHT:
  1410       case FULLSCREEN_MODE_WIDTH:
  1411         SetFlags (Flags () | B_NOT_ZOOMABLE | B_NOT_RESIZABLE);
  1412         pre_fullscreen_rect = frame;
  1413         zoom_rect = FullscreenRectForMode (mode);
  1414         ResizeTo (BE_RECT_WIDTH (zoom_rect) - 1,
  1415                   BE_RECT_HEIGHT (zoom_rect) - 1);
  1416         MoveTo (zoom_rect.left, zoom_rect.top);
  1417         break;
  1418 
  1419       case FULLSCREEN_MODE_NONE:
  1420         break;
  1421       }
  1422 
  1423     fullscreen_mode = mode;
  1424   }
  1425 
  1426   void
  1427   Zoom (BPoint origin, float width, float height)
  1428   {
  1429     struct haiku_zoom_event rq;
  1430 
  1431     rq.window = this;
  1432     rq.fullscreen_mode = fullscreen_mode;
  1433     haiku_write (ZOOM_EVENT, &rq);
  1434   }
  1435 
  1436   void
  1437   OffsetChildRect (BRect *r, EmacsWindow *c)
  1438   {
  1439     if (!child_frame_lock.Lock ())
  1440       gui_abort ("Failed to lock child frame state lock");
  1441 
  1442     for (struct child_frame *f; f; f = f->next)
  1443       if (f->window == c)
  1444         {
  1445           r->top -= f->yoff;
  1446           r->bottom -= f->yoff;
  1447           r->left -= f->xoff;
  1448           r->right -= f->xoff;
  1449           child_frame_lock.Unlock ();
  1450           return;
  1451         }
  1452 
  1453     child_frame_lock.Lock ();
  1454     gui_abort ("Trying to calculate offsets for a child frame that doesn't exist");
  1455   }
  1456 };
  1457 
  1458 class EmacsMenuBar : public BMenuBar
  1459 {
  1460   bool tracking_p;
  1461 
  1462 public:
  1463   EmacsMenuBar () : BMenuBar (BRect (0, 0, 0, 0), NULL)
  1464   {
  1465   }
  1466 
  1467   void
  1468   AttachedToWindow (void)
  1469   {
  1470     BWindow *window = Window ();
  1471 
  1472     window->SetKeyMenuBar (this);
  1473   }
  1474 
  1475   void
  1476   FrameResized (float newWidth, float newHeight)
  1477   {
  1478     struct haiku_menu_bar_resize_event rq;
  1479     rq.window = this->Window ();
  1480     rq.height = std::lrint (newHeight + 1);
  1481     rq.width = std::lrint (newWidth + 1);
  1482 
  1483     haiku_write (MENU_BAR_RESIZE, &rq);
  1484     BMenuBar::FrameResized (newWidth, newHeight);
  1485   }
  1486 
  1487   void
  1488   MouseDown (BPoint point)
  1489   {
  1490     struct haiku_menu_bar_click_event rq;
  1491     EmacsWindow *ew = (EmacsWindow *) Window ();
  1492 
  1493     rq.window = ew;
  1494     rq.x = std::lrint (point.x);
  1495     rq.y = std::lrint (point.y);
  1496 
  1497     if (!ew->menu_bar_active_p)
  1498       haiku_write (MENU_BAR_CLICK, &rq);
  1499     else
  1500       BMenuBar::MouseDown (point);
  1501   }
  1502 
  1503   void
  1504   MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
  1505   {
  1506     struct haiku_menu_bar_left_event rq;
  1507 
  1508     if (transit == B_EXITED_VIEW)
  1509       {
  1510         rq.x = std::lrint (point.x);
  1511         rq.y = std::lrint (point.y);
  1512         rq.window = this->Window ();
  1513 
  1514         haiku_write (MENU_BAR_LEFT, &rq);
  1515       }
  1516 
  1517     BMenuBar::MouseMoved (point, transit, msg);
  1518   }
  1519 
  1520   void
  1521   MessageReceived (BMessage *msg)
  1522   {
  1523     BRect frame;
  1524     BPoint pt, l;
  1525     EmacsWindow *window;
  1526     bool menus_begun;
  1527 
  1528     if (msg->what == SHOW_MENU_BAR)
  1529       {
  1530         window = (EmacsWindow *) Window ();
  1531         frame = Frame ();
  1532         pt = frame.LeftTop ();
  1533         l = pt;
  1534         menus_begun = false;
  1535         Parent ()->ConvertToScreen (&pt);
  1536 
  1537         window->menus_begun = &menus_begun;
  1538         set_mouse_position (pt.x, pt.y);
  1539         BMenuBar::MouseDown (l);
  1540         window->menus_begun = NULL;
  1541 
  1542         if (!menus_begun)
  1543           msg->SendReply (msg);
  1544         else
  1545           msg->SendReply (BE_MENU_BAR_OPEN);
  1546       }
  1547     else if (msg->what == REPLAY_MENU_BAR)
  1548       {
  1549         window = (EmacsWindow *) Window ();
  1550         menus_begun = false;
  1551         window->menus_begun = &menus_begun;
  1552 
  1553         if (msg->FindPoint ("emacs:point", &pt) == B_OK)
  1554           BMenuBar::MouseDown (pt);
  1555 
  1556         window->menus_begun = NULL;
  1557 
  1558         if (!menus_begun)
  1559           msg->SendReply (msg);
  1560         else
  1561           msg->SendReply (BE_MENU_BAR_OPEN);
  1562       }
  1563     else
  1564       BMenuBar::MessageReceived (msg);
  1565   }
  1566 };
  1567 
  1568 class EmacsView : public BView
  1569 {
  1570 public:
  1571   int looper_locked_count;
  1572   BRegion sb_region;
  1573   BRegion invalid_region;
  1574 
  1575   BView *offscreen_draw_view;
  1576   BBitmap *offscreen_draw_bitmap_1;
  1577   BBitmap *copy_bitmap;
  1578 
  1579 #ifdef USE_BE_CAIRO
  1580   cairo_surface_t *cr_surface;
  1581   cairo_t *cr_context;
  1582   BLocker cr_surface_lock;
  1583 #endif
  1584 
  1585   BMessage *wait_for_release_message;
  1586   int64 grabbed_buttons;
  1587   BScreen screen;
  1588   bool use_frame_synchronization;
  1589 
  1590   EmacsView () : BView (BRect (0, 0, 0, 0), "Emacs",
  1591                         B_FOLLOW_NONE, B_WILL_DRAW),
  1592                  looper_locked_count (0),
  1593                  offscreen_draw_view (NULL),
  1594                  offscreen_draw_bitmap_1 (NULL),
  1595                  copy_bitmap (NULL),
  1596 #ifdef USE_BE_CAIRO
  1597                  cr_surface (NULL),
  1598                  cr_context (NULL),
  1599 #endif
  1600                  wait_for_release_message (NULL),
  1601                  grabbed_buttons (0),
  1602                  use_frame_synchronization (false)
  1603   {
  1604 
  1605   }
  1606 
  1607   ~EmacsView ()
  1608   {
  1609     if (wait_for_release_message)
  1610       {
  1611         wait_for_release_message->SendReply (wait_for_release_message);
  1612         delete wait_for_release_message;
  1613       }
  1614 
  1615     TearDownDoubleBuffering ();
  1616 
  1617     if (!grab_view_locker.Lock ())
  1618       gui_abort ("Couldn't lock grab view locker");
  1619     if (grab_view == this)
  1620       grab_view = NULL;
  1621     grab_view_locker.Unlock ();
  1622   }
  1623 
  1624   void
  1625   SetFrameSynchronization (bool sync)
  1626   {
  1627     if (LockLooper ())
  1628       {
  1629         use_frame_synchronization = sync;
  1630         UnlockLooper ();
  1631       }
  1632   }
  1633 
  1634   void
  1635   MessageReceived (BMessage *msg)
  1636   {
  1637     uint32 buttons;
  1638     BLooper *looper = Looper ();
  1639 
  1640     if (msg->what == WAIT_FOR_RELEASE)
  1641       {
  1642         if (wait_for_release_message)
  1643           gui_abort ("Wait for release message already exists");
  1644 
  1645         GetMouse (NULL, &buttons, false);
  1646 
  1647         if (!buttons)
  1648           msg->SendReply (msg);
  1649         else
  1650           wait_for_release_message = looper->DetachCurrentMessage ();
  1651       }
  1652     else if (msg->what == RELEASE_NOW)
  1653       {
  1654         if (wait_for_release_message)
  1655           wait_for_release_message->SendReply (msg);
  1656 
  1657         delete wait_for_release_message;
  1658         wait_for_release_message = NULL;
  1659       }
  1660     else
  1661       BView::MessageReceived (msg);
  1662   }
  1663 
  1664 #ifdef USE_BE_CAIRO
  1665   void
  1666   DetachCairoSurface (void)
  1667   {
  1668     if (!cr_surface_lock.Lock ())
  1669       gui_abort ("Could not lock cr surface during detachment");
  1670     if (!cr_surface)
  1671       gui_abort ("Trying to detach window cr surface when none exists");
  1672     cairo_destroy (cr_context);
  1673     cairo_surface_destroy (cr_surface);
  1674     cr_surface = NULL;
  1675     cr_context = NULL;
  1676     cr_surface_lock.Unlock ();
  1677   }
  1678 
  1679   void
  1680   AttachCairoSurface (void)
  1681   {
  1682     if (!cr_surface_lock.Lock ())
  1683       gui_abort ("Could not lock cr surface during attachment");
  1684     if (cr_surface)
  1685       gui_abort ("Trying to attach cr surface when one already exists");
  1686     BRect bounds = offscreen_draw_bitmap_1->Bounds ();
  1687 
  1688     cr_surface = cairo_image_surface_create_for_data
  1689       ((unsigned char *) offscreen_draw_bitmap_1->Bits (),
  1690        CAIRO_FORMAT_ARGB32, BE_RECT_WIDTH (bounds),
  1691        BE_RECT_HEIGHT (bounds),
  1692        offscreen_draw_bitmap_1->BytesPerRow ());
  1693     if (!cr_surface)
  1694       gui_abort ("Cr surface allocation failed for double-buffered view");
  1695 
  1696     cr_context = cairo_create (cr_surface);
  1697     if (!cr_context)
  1698       gui_abort ("cairo_t allocation failed for double-buffered view");
  1699     cr_surface_lock.Unlock ();
  1700   }
  1701 #endif
  1702 
  1703   void
  1704   TearDownDoubleBuffering (void)
  1705   {
  1706     if (offscreen_draw_view)
  1707       {
  1708         if (Window ())
  1709           ClearViewBitmap ();
  1710         if (copy_bitmap)
  1711           {
  1712             delete copy_bitmap;
  1713             copy_bitmap = NULL;
  1714           }
  1715         if (!looper_locked_count)
  1716           if (!offscreen_draw_view->LockLooper ())
  1717             gui_abort ("Failed to lock offscreen draw view");
  1718 #ifdef USE_BE_CAIRO
  1719         if (cr_surface)
  1720           DetachCairoSurface ();
  1721 #endif
  1722         offscreen_draw_view->RemoveSelf ();
  1723         delete offscreen_draw_view;
  1724         offscreen_draw_view = NULL;
  1725         delete offscreen_draw_bitmap_1;
  1726         offscreen_draw_bitmap_1 = NULL;
  1727       }
  1728    }
  1729 
  1730   void
  1731   AfterResize (void)
  1732   {
  1733     if (offscreen_draw_view)
  1734       {
  1735         if (!LockLooper ())
  1736           gui_abort ("Failed to lock looper after resize");
  1737 
  1738         if (!offscreen_draw_view->LockLooper ())
  1739           gui_abort ("Failed to lock offscreen draw view after resize");
  1740 #ifdef USE_BE_CAIRO
  1741         DetachCairoSurface ();
  1742 #endif
  1743         offscreen_draw_view->RemoveSelf ();
  1744         delete offscreen_draw_bitmap_1;
  1745         offscreen_draw_bitmap_1 = new BBitmap (Frame (), B_RGBA32, 1);
  1746         if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
  1747           gui_abort ("Offscreen draw bitmap initialization failed");
  1748 
  1749         BRect frame = Frame ();
  1750 
  1751         offscreen_draw_view->MoveTo (frame.left, frame.top);
  1752         offscreen_draw_view->ResizeTo (BE_RECT_WIDTH (frame),
  1753                                        BE_RECT_HEIGHT (frame));
  1754         offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
  1755 #ifdef USE_BE_CAIRO
  1756         AttachCairoSurface ();
  1757 #endif
  1758 
  1759         if (looper_locked_count)
  1760           offscreen_draw_bitmap_1->Lock ();
  1761 
  1762         UnlockLooper ();
  1763       }
  1764   }
  1765 
  1766   void
  1767   Draw (BRect expose_bounds)
  1768   {
  1769     struct haiku_expose_event rq;
  1770     EmacsWindow *w = (EmacsWindow *) Window ();
  1771 
  1772     if (w->shown_flag && offscreen_draw_view)
  1773       {
  1774         PushState ();
  1775         SetDrawingMode (B_OP_ERASE);
  1776         FillRect (Frame ());
  1777         PopState ();
  1778         return;
  1779       }
  1780 
  1781     if (!offscreen_draw_view)
  1782       {
  1783         if (sb_region.Contains (std::lrint (expose_bounds.left),
  1784                                 std::lrint (expose_bounds.top)) &&
  1785             sb_region.Contains (std::lrint (expose_bounds.right),
  1786                                 std::lrint (expose_bounds.top)) &&
  1787             sb_region.Contains (std::lrint (expose_bounds.left),
  1788                                 std::lrint (expose_bounds.bottom)) &&
  1789             sb_region.Contains (std::lrint (expose_bounds.right),
  1790                                 std::lrint (expose_bounds.bottom)))
  1791           return;
  1792 
  1793         rq.x = std::floor (expose_bounds.left);
  1794         rq.y = std::floor (expose_bounds.top);
  1795         rq.width = std::ceil (expose_bounds.right - expose_bounds.left + 1);
  1796         rq.height = std::ceil (expose_bounds.bottom - expose_bounds.top + 1);
  1797         if (!rq.width)
  1798           rq.width = 1;
  1799         if (!rq.height)
  1800           rq.height = 1;
  1801         rq.window = this->Window ();
  1802 
  1803         haiku_write (FRAME_EXPOSED, &rq);
  1804       }
  1805   }
  1806 
  1807   void
  1808   FlipBuffers (void)
  1809   {
  1810     EmacsWindow *w;
  1811     if (!LockLooper ())
  1812       gui_abort ("Failed to lock looper during buffer flip");
  1813     if (!offscreen_draw_view)
  1814       gui_abort ("Failed to lock offscreen view during buffer flip");
  1815 
  1816     offscreen_draw_view->Sync ();
  1817     w = (EmacsWindow *) Window ();
  1818     w->shown_flag = 0;
  1819 
  1820     if (copy_bitmap &&
  1821         copy_bitmap->Bounds () != offscreen_draw_bitmap_1->Bounds ())
  1822       {
  1823         delete copy_bitmap;
  1824         copy_bitmap = NULL;
  1825       }
  1826     if (!copy_bitmap)
  1827       {
  1828         copy_bitmap = new BBitmap (offscreen_draw_bitmap_1);
  1829         SetViewBitmap (copy_bitmap, Frame (),
  1830                        Frame (), B_FOLLOW_NONE, 0);
  1831       }
  1832     else
  1833       copy_bitmap->ImportBits (offscreen_draw_bitmap_1);
  1834 
  1835     if (copy_bitmap->InitCheck () != B_OK)
  1836       gui_abort ("Failed to init copy bitmap during buffer flip");
  1837 
  1838     /* Wait for VBLANK.  If responding to the invalidation or buffer
  1839        flipping takes longer than the blanking period, we lose.  */
  1840     if (use_frame_synchronization)
  1841       screen.WaitForRetrace ();
  1842 
  1843     Invalidate (&invalid_region);
  1844     invalid_region.MakeEmpty ();
  1845     UnlockLooper ();
  1846     return;
  1847   }
  1848 
  1849   void
  1850   SetUpDoubleBuffering (void)
  1851   {
  1852     if (!LockLooper ())
  1853       gui_abort ("Failed to lock self setting up double buffering");
  1854     if (offscreen_draw_view)
  1855       gui_abort ("Failed to lock offscreen view setting up double buffering");
  1856 
  1857     offscreen_draw_bitmap_1 = new BBitmap (Frame (), B_RGBA32, 1);
  1858     if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
  1859       gui_abort ("Failed to init offscreen bitmap");
  1860 #ifdef USE_BE_CAIRO
  1861     AttachCairoSurface ();
  1862 #endif
  1863     offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
  1864     offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
  1865 
  1866     if (looper_locked_count)
  1867       {
  1868         if (!offscreen_draw_bitmap_1->Lock ())
  1869           gui_abort ("Failed to lock bitmap after double buffering was set up");
  1870       }
  1871 
  1872     invalid_region.MakeEmpty ();
  1873     UnlockLooper ();
  1874     Invalidate ();
  1875   }
  1876 
  1877   void
  1878   MouseMoved (BPoint point, uint32 transit, const BMessage *drag_msg)
  1879   {
  1880     struct haiku_mouse_motion_event rq;
  1881     int64 threadid;
  1882     EmacsWindow *window;
  1883 
  1884     window = (EmacsWindow *) Window ();
  1885 
  1886     if (transit == B_EXITED_VIEW)
  1887       rq.just_exited_p = true;
  1888     else
  1889       rq.just_exited_p = false;
  1890 
  1891     rq.x = point.x;
  1892     rq.y = point.y;
  1893     rq.window = window;
  1894     rq.time = system_time ();
  1895 
  1896     if (drag_msg && (drag_msg->IsSourceRemote ()
  1897                      || drag_msg->FindInt64 ("emacs:thread_id",
  1898                                              &threadid) != B_OK
  1899                      || threadid != find_thread (NULL)))
  1900       rq.dnd_message = true;
  1901     else
  1902       rq.dnd_message = false;
  1903 
  1904     if (!grab_view_locker.Lock ())
  1905       gui_abort ("Couldn't lock grab view locker");
  1906 
  1907     if (grab_view && this != grab_view)
  1908       {
  1909         grab_view_locker.Unlock ();
  1910         return;
  1911       }
  1912 
  1913     grab_view_locker.Unlock ();
  1914 
  1915     haiku_write (MOUSE_MOTION, &rq);
  1916   }
  1917 
  1918   void
  1919   BasicMouseDown (BPoint point, BView *scroll_bar, BMessage *message)
  1920   {
  1921     struct haiku_button_event rq;
  1922     int64 when;
  1923     int32 mods, buttons, button;
  1924 
  1925     if (message->FindInt64 ("when", &when) != B_OK
  1926         || message->FindInt32 ("modifiers", &mods) != B_OK
  1927         || message->FindInt32 ("buttons", &buttons) != B_OK)
  1928       return;
  1929 
  1930     /* Find which button was pressed by comparing the previous button
  1931        mask to the current one.  This assumes that B_MOUSE_DOWN will
  1932        be sent for each button press.  */
  1933     button = buttons & ~grabbed_buttons;
  1934     grabbed_buttons = buttons;
  1935 
  1936     if (!scroll_bar)
  1937       {
  1938         if (!grab_view_locker.Lock ())
  1939           gui_abort ("Couldn't lock grab view locker");
  1940         grab_view = this;
  1941         grab_view_locker.Unlock ();
  1942       }
  1943 
  1944     rq.window = this->Window ();
  1945     rq.scroll_bar = scroll_bar;
  1946 
  1947     if (button == B_PRIMARY_MOUSE_BUTTON)
  1948       rq.btn_no = 0;
  1949     else if (button == B_SECONDARY_MOUSE_BUTTON)
  1950       rq.btn_no = 2;
  1951     else if (button == B_TERTIARY_MOUSE_BUTTON)
  1952       rq.btn_no = 1;
  1953     else
  1954       /* We don't know which button was pressed.  This usually happens
  1955          when a B_MOUSE_UP is sent to a view that didn't receive a
  1956          corresponding B_MOUSE_DOWN event, so simply ignore the
  1957          message.  */
  1958       return;
  1959 
  1960     rq.x = point.x;
  1961     rq.y = point.y;
  1962     rq.modifiers = 0;
  1963 
  1964     if (mods & B_SHIFT_KEY)
  1965       rq.modifiers |= HAIKU_MODIFIER_SHIFT;
  1966 
  1967     if (mods & B_CONTROL_KEY)
  1968       rq.modifiers |= HAIKU_MODIFIER_CTRL;
  1969 
  1970     if (mods & B_COMMAND_KEY)
  1971       rq.modifiers |= HAIKU_MODIFIER_ALT;
  1972 
  1973     if (mods & B_OPTION_KEY)
  1974       rq.modifiers |= HAIKU_MODIFIER_SUPER;
  1975 
  1976     if (!scroll_bar)
  1977       SetMouseEventMask (B_POINTER_EVENTS, (B_LOCK_WINDOW_FOCUS
  1978                                             | B_NO_POINTER_HISTORY));
  1979 
  1980     rq.time = when;
  1981     haiku_write (BUTTON_DOWN, &rq);
  1982   }
  1983 
  1984   void
  1985   MouseDown (BPoint point)
  1986   {
  1987     BMessage *msg;
  1988     BLooper *looper;
  1989 
  1990     looper = Looper ();
  1991     msg = (looper
  1992            ? looper->CurrentMessage ()
  1993            : NULL);
  1994 
  1995     if (msg)
  1996       BasicMouseDown (point, NULL, msg);
  1997   }
  1998 
  1999   void
  2000   BasicMouseUp (BPoint point, BView *scroll_bar, BMessage *message)
  2001   {
  2002     struct haiku_button_event rq;
  2003     int64 when;
  2004     int32 mods, button, buttons;
  2005 
  2006     if (message->FindInt64 ("when", &when) != B_OK
  2007         || message->FindInt32 ("modifiers", &mods) != B_OK
  2008         || message->FindInt32 ("buttons", &buttons) != B_OK)
  2009       return;
  2010 
  2011     if (!scroll_bar)
  2012       {
  2013         if (!grab_view_locker.Lock ())
  2014           gui_abort ("Couldn't lock grab view locker");
  2015         if (!buttons)
  2016           grab_view = NULL;
  2017         grab_view_locker.Unlock ();
  2018       }
  2019 
  2020     button = (grabbed_buttons & ~buttons);
  2021     grabbed_buttons = buttons;
  2022 
  2023     if (wait_for_release_message)
  2024       {
  2025         if (!grabbed_buttons)
  2026           {
  2027             wait_for_release_message->SendReply (wait_for_release_message);
  2028             delete wait_for_release_message;
  2029             wait_for_release_message = NULL;
  2030           }
  2031 
  2032         return;
  2033       }
  2034 
  2035     rq.window = this->Window ();
  2036     rq.scroll_bar = scroll_bar;
  2037 
  2038     if (button == B_PRIMARY_MOUSE_BUTTON)
  2039       rq.btn_no = 0;
  2040     else if (button == B_SECONDARY_MOUSE_BUTTON)
  2041       rq.btn_no = 2;
  2042     else if (button == B_TERTIARY_MOUSE_BUTTON)
  2043       rq.btn_no = 1;
  2044     else
  2045       return;
  2046 
  2047     rq.x = point.x;
  2048     rq.y = point.y;
  2049 
  2050     rq.modifiers = 0;
  2051     if (mods & B_SHIFT_KEY)
  2052       rq.modifiers |= HAIKU_MODIFIER_SHIFT;
  2053 
  2054     if (mods & B_CONTROL_KEY)
  2055       rq.modifiers |= HAIKU_MODIFIER_CTRL;
  2056 
  2057     if (mods & B_COMMAND_KEY)
  2058       rq.modifiers |= HAIKU_MODIFIER_ALT;
  2059 
  2060     if (mods & B_OPTION_KEY)
  2061       rq.modifiers |= HAIKU_MODIFIER_SUPER;
  2062 
  2063     rq.time = when;
  2064     haiku_write (BUTTON_UP, &rq);
  2065   }
  2066 
  2067   void
  2068   MouseUp (BPoint point)
  2069   {
  2070     BMessage *msg;
  2071     BLooper *looper;
  2072 
  2073     looper = Looper ();
  2074     msg = (looper
  2075            ? looper->CurrentMessage ()
  2076            : NULL);
  2077 
  2078     if (msg)
  2079       BasicMouseUp (point, NULL, msg);
  2080   }
  2081 };
  2082 
  2083 class EmacsScrollBar : public BScrollBar
  2084 {
  2085 public:
  2086   int dragging;
  2087   bool horizontal;
  2088   enum haiku_scroll_bar_part current_part;
  2089   float old_value;
  2090   scroll_bar_info info;
  2091 
  2092   /* How many button events were passed to the parent without
  2093      release.  */
  2094   int handle_button_count;
  2095   bool in_overscroll;
  2096   bool can_overscroll;
  2097   bool maybe_overscroll;
  2098   BPoint last_overscroll;
  2099   int last_reported_overscroll_value;
  2100   int max_value, real_max_value;
  2101   int overscroll_start_value;
  2102   bigtime_t repeater_start;
  2103   EmacsView *parent;
  2104 
  2105   EmacsScrollBar (int x, int y, int x1, int y1, bool horizontal_p,
  2106                   EmacsView *parent)
  2107     : BScrollBar (BRect (x, y, x1, y1), NULL, NULL, 0, 0, horizontal_p ?
  2108                   B_HORIZONTAL : B_VERTICAL),
  2109       dragging (0),
  2110       handle_button_count (0),
  2111       in_overscroll (false),
  2112       can_overscroll (false),
  2113       maybe_overscroll (false),
  2114       parent (parent)
  2115   {
  2116     BView *vw = (BView *) this;
  2117     vw->SetResizingMode (B_FOLLOW_NONE);
  2118     horizontal = horizontal_p;
  2119     get_scroll_bar_info (&info);
  2120     SetSteps (5000, 10000);
  2121   }
  2122 
  2123   void
  2124   MessageReceived (BMessage *msg)
  2125   {
  2126     int32 portion, range, dragging, value;
  2127     float proportion;
  2128 
  2129     if (msg->what == SCROLL_BAR_UPDATE)
  2130       {
  2131         portion = msg->GetInt32 ("emacs:portion", 0);
  2132         range = msg->GetInt32 ("emacs:range", 0);
  2133         dragging = msg->GetInt32 ("emacs:dragging", 0);
  2134         proportion = ((range <= 0 || portion <= 0)
  2135                       ? 1.0f : (float) portion / range);
  2136         value = msg->GetInt32 ("emacs:units", 0);
  2137         can_overscroll = msg->GetBool ("emacs:overscroll", false);
  2138 
  2139         if (value < 0)
  2140           value = 0;
  2141 
  2142         if (dragging != 1)
  2143           {
  2144             if (in_overscroll || dragging != -1)
  2145               {
  2146                 /* Set the value to the smallest possible one.
  2147                    Otherwise, the call to SetRange could lead to
  2148                    spurious updates.  */
  2149                 old_value = 0;
  2150                 SetValue (0);
  2151 
  2152                 /* Unlike on Motif, PORTION isn't included in the total
  2153                    range of the scroll bar.  */
  2154 
  2155                 SetRange (0, range - portion);
  2156                 SetProportion (proportion);
  2157                 max_value = range - portion;
  2158                 real_max_value = range;
  2159 
  2160                 if (in_overscroll || value > max_value)
  2161                   value = max_value;
  2162 
  2163                 old_value = roundf (value);
  2164                 SetValue (old_value);
  2165               }
  2166             else
  2167               {
  2168                 value = Value ();
  2169 
  2170                 old_value = 0;
  2171                 SetValue (0);
  2172                 SetRange (0, range - portion);
  2173                 SetProportion (proportion);
  2174                 old_value = value;
  2175                 SetValue (value);
  2176                 max_value = range - portion;
  2177                 real_max_value = range;
  2178               }
  2179           }
  2180       }
  2181 
  2182     BScrollBar::MessageReceived (msg);
  2183   }
  2184 
  2185   void
  2186   Pulse (void)
  2187   {
  2188     struct haiku_scroll_bar_part_event rq;
  2189     BPoint point;
  2190     uint32 buttons;
  2191 
  2192     if (!dragging)
  2193       {
  2194         SetFlags (Flags () & ~B_PULSE_NEEDED);
  2195         return;
  2196       }
  2197 
  2198     if (repeater_start < system_time ())
  2199       {
  2200         GetMouse (&point, &buttons, false);
  2201 
  2202         if (ButtonRegionFor (current_part).Contains (point))
  2203           {
  2204             rq.scroll_bar = this;
  2205             rq.window = Window ();
  2206             rq.part = current_part;
  2207             haiku_write (SCROLL_BAR_PART_EVENT, &rq);
  2208           }
  2209       }
  2210 
  2211     BScrollBar::Pulse ();
  2212   }
  2213 
  2214   void
  2215   ValueChanged (float new_value)
  2216   {
  2217     struct haiku_scroll_bar_value_event rq;
  2218 
  2219     new_value = Value ();
  2220 
  2221     if (dragging)
  2222       {
  2223         if (new_value != old_value)
  2224           {
  2225             if (dragging > 1)
  2226               {
  2227                 SetValue (old_value);
  2228                 SetFlags (Flags () | B_PULSE_NEEDED);
  2229               }
  2230             else
  2231               dragging++;
  2232           }
  2233 
  2234         return;
  2235       }
  2236 
  2237     if (new_value != old_value)
  2238       {
  2239         rq.scroll_bar = this;
  2240         rq.window = Window ();
  2241         rq.position = new_value;
  2242         old_value = new_value;
  2243 
  2244         haiku_write (SCROLL_BAR_VALUE_EVENT, &rq);
  2245       }
  2246   }
  2247 
  2248   BRegion
  2249   ButtonRegionFor (enum haiku_scroll_bar_part button)
  2250   {
  2251     BRegion region;
  2252     BRect bounds;
  2253     BRect rect;
  2254     float button_size;
  2255 
  2256     bounds = Bounds ();
  2257     bounds.InsetBy (0.0, 0.0);
  2258 
  2259     if (horizontal)
  2260       button_size = bounds.Height () + 1.0f;
  2261     else
  2262       button_size = bounds.Width () + 1.0f;
  2263 
  2264     rect = BRect (bounds.left, bounds.top,
  2265                   bounds.left + button_size - 1.0f,
  2266                   bounds.top + button_size - 1.0f);
  2267 
  2268     if (button == HAIKU_SCROLL_BAR_UP_BUTTON)
  2269       {
  2270         if (!horizontal)
  2271           {
  2272             region.Include (rect);
  2273             if (info.double_arrows)
  2274               region.Include (rect.OffsetToCopy (bounds.left,
  2275                                                  bounds.bottom - 2 * button_size + 1));
  2276           }
  2277         else
  2278           {
  2279             region.Include (rect);
  2280             if (info.double_arrows)
  2281               region.Include (rect.OffsetToCopy (bounds.right - 2 * button_size,
  2282                                                  bounds.top));
  2283           }
  2284       }
  2285     else
  2286       {
  2287         if (!horizontal)
  2288           {
  2289             region.Include (rect.OffsetToCopy (bounds.left, bounds.bottom - button_size));
  2290 
  2291             if (info.double_arrows)
  2292               region.Include (rect.OffsetByCopy (0.0, button_size));
  2293           }
  2294         else
  2295           {
  2296             region.Include (rect.OffsetToCopy (bounds.right - button_size, bounds.top));
  2297 
  2298             if (info.double_arrows)
  2299               region.Include (rect.OffsetByCopy (button_size, 0.0));
  2300           }
  2301       }
  2302 
  2303     return region;
  2304   }
  2305 
  2306   void
  2307   MouseDown (BPoint pt)
  2308   {
  2309     struct haiku_scroll_bar_drag_event rq;
  2310     struct haiku_scroll_bar_part_event part;
  2311     BRegion r;
  2312     BLooper *looper;
  2313     BMessage *message;
  2314     int32 buttons, mods;
  2315 
  2316     looper = Looper ();
  2317     message = NULL;
  2318 
  2319     if (!looper)
  2320       GetMouse (&pt, (uint32 *) &buttons, false);
  2321     else
  2322       {
  2323         message = looper->CurrentMessage ();
  2324 
  2325         if (!message || message->FindInt32 ("buttons", &buttons) != B_OK)
  2326           GetMouse (&pt, (uint32 *) &buttons, false);
  2327       }
  2328 
  2329     if (message && (message->FindInt32 ("modifiers", &mods)
  2330                     == B_OK)
  2331         && mods & B_CONTROL_KEY)
  2332       {
  2333         /* Allow C-mouse-3 to split the window on a scroll bar.   */
  2334         handle_button_count += 1;
  2335         SetMouseEventMask (B_POINTER_EVENTS, (B_SUSPEND_VIEW_FOCUS
  2336                                               | B_LOCK_WINDOW_FOCUS));
  2337         parent->BasicMouseDown (ConvertToParent (pt), this, message);
  2338 
  2339         return;
  2340       }
  2341 
  2342     repeater_start = system_time () + 300000;
  2343 
  2344     if (buttons == B_PRIMARY_MOUSE_BUTTON)
  2345       {
  2346         r = ButtonRegionFor (HAIKU_SCROLL_BAR_UP_BUTTON);
  2347 
  2348         if (r.Contains (pt))
  2349           {
  2350             part.scroll_bar = this;
  2351             part.window = Window ();
  2352             part.part = HAIKU_SCROLL_BAR_UP_BUTTON;
  2353             dragging = 1;
  2354             current_part = HAIKU_SCROLL_BAR_UP_BUTTON;
  2355 
  2356             haiku_write (SCROLL_BAR_PART_EVENT, &part);
  2357             goto out;
  2358           }
  2359 
  2360         r = ButtonRegionFor (HAIKU_SCROLL_BAR_DOWN_BUTTON);
  2361 
  2362         if (r.Contains (pt))
  2363           {
  2364             part.scroll_bar = this;
  2365             part.window = Window ();
  2366             part.part = HAIKU_SCROLL_BAR_DOWN_BUTTON;
  2367             dragging = 1;
  2368             current_part = HAIKU_SCROLL_BAR_DOWN_BUTTON;
  2369 
  2370             if (Value () == max_value)
  2371               {
  2372                 SetFlags (Flags () | B_PULSE_NEEDED);
  2373                 dragging = 2;
  2374               }
  2375 
  2376             haiku_write (SCROLL_BAR_PART_EVENT, &part);
  2377             goto out;
  2378           }
  2379 
  2380         maybe_overscroll = true;
  2381       }
  2382 
  2383     rq.dragging_p = 1;
  2384     rq.window = Window ();
  2385     rq.scroll_bar = this;
  2386 
  2387     SetMouseEventMask (B_POINTER_EVENTS, (B_SUSPEND_VIEW_FOCUS
  2388                                           | B_LOCK_WINDOW_FOCUS));
  2389 
  2390     haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
  2391 
  2392   out:
  2393     BScrollBar::MouseDown (pt);
  2394   }
  2395 
  2396   void
  2397   MouseUp (BPoint pt)
  2398   {
  2399     struct haiku_scroll_bar_drag_event rq;
  2400     BMessage *msg;
  2401     BLooper *looper;
  2402 
  2403     in_overscroll = false;
  2404     maybe_overscroll = false;
  2405 
  2406     if (handle_button_count)
  2407       {
  2408         handle_button_count--;
  2409         looper = Looper ();
  2410         msg = (looper
  2411                ? looper->CurrentMessage ()
  2412                : NULL);
  2413 
  2414         if (msg)
  2415           parent->BasicMouseUp (ConvertToParent (pt),
  2416                                 this, msg);
  2417 
  2418         return;
  2419       }
  2420 
  2421     rq.dragging_p = 0;
  2422     rq.scroll_bar = this;
  2423     rq.window = Window ();
  2424 
  2425     haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
  2426     dragging = 0;
  2427 
  2428     BScrollBar::MouseUp (pt);
  2429   }
  2430 
  2431   void
  2432   MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
  2433   {
  2434     struct haiku_menu_bar_left_event rq;
  2435     struct haiku_scroll_bar_value_event value_event;
  2436     int range, diff, value, trough_size;
  2437     BRect bounds;
  2438     BPoint conv;
  2439     uint32 buttons;
  2440 
  2441     GetMouse (NULL, &buttons, false);
  2442 
  2443     if (transit == B_EXITED_VIEW)
  2444       {
  2445         conv = ConvertToParent (point);
  2446 
  2447         rq.x = std::lrint (conv.x);
  2448         rq.y = std::lrint (conv.y);
  2449         rq.window = this->Window ();
  2450 
  2451         haiku_write (MENU_BAR_LEFT, &rq);
  2452       }
  2453 
  2454     if (in_overscroll)
  2455       {
  2456         if (horizontal)
  2457           diff = point.x - last_overscroll.x;
  2458         else
  2459           diff = point.y - last_overscroll.y;
  2460 
  2461         if (diff < 0)
  2462           {
  2463             in_overscroll = false;
  2464             goto allow;
  2465           }
  2466 
  2467         range = real_max_value;
  2468         bounds = Bounds ();
  2469         bounds.InsetBy (1.0, 1.0);
  2470         value = overscroll_start_value;
  2471         trough_size = (horizontal
  2472                        ? BE_RECT_WIDTH (bounds)
  2473                        : BE_RECT_HEIGHT (bounds));
  2474         trough_size -= (horizontal
  2475                         ? BE_RECT_HEIGHT (bounds)
  2476                         : BE_RECT_WIDTH (bounds)) / 2;
  2477         if (info.double_arrows)
  2478           trough_size -= (horizontal
  2479                           ? BE_RECT_HEIGHT (bounds)
  2480                           : BE_RECT_WIDTH (bounds)) / 2;
  2481 
  2482         value += ((double) range / trough_size) * diff;
  2483 
  2484         if (value != last_reported_overscroll_value)
  2485           {
  2486             last_reported_overscroll_value = value;
  2487 
  2488             value_event.scroll_bar = this;
  2489             value_event.window = Window ();
  2490             value_event.position = value;
  2491 
  2492             haiku_write (SCROLL_BAR_VALUE_EVENT, &value_event);
  2493             return;
  2494           }
  2495       }
  2496     else if (can_overscroll
  2497              && (buttons == B_PRIMARY_MOUSE_BUTTON)
  2498              && maybe_overscroll)
  2499       {
  2500         value = Value ();
  2501 
  2502         if (value >= max_value)
  2503           {
  2504             BScrollBar::MouseMoved (point, transit, msg);
  2505 
  2506             if (value == Value ())
  2507               {
  2508                 overscroll_start_value = value;
  2509                 in_overscroll = true;
  2510                 last_overscroll = point;
  2511                 last_reported_overscroll_value = value;
  2512 
  2513                 MouseMoved (point, transit, msg);
  2514                 return;
  2515               }
  2516           }
  2517       }
  2518 
  2519   allow:
  2520     BScrollBar::MouseMoved (point, transit, msg);
  2521   }
  2522 };
  2523 
  2524 class EmacsTitleMenuItem : public BMenuItem
  2525 {
  2526 public:
  2527   EmacsTitleMenuItem (const char *str) : BMenuItem (str, NULL)
  2528   {
  2529     SetEnabled (0);
  2530   }
  2531 
  2532   void
  2533   DrawContent (void)
  2534   {
  2535     BMenu *menu = Menu ();
  2536 
  2537     menu->PushState ();
  2538     menu->SetFont (be_bold_font);
  2539     menu->SetHighColor (ui_color (B_MENU_ITEM_TEXT_COLOR));
  2540     BMenuItem::DrawContent ();
  2541     menu->PopState ();
  2542   }
  2543 };
  2544 
  2545 class EmacsMenuItem : public BMenuItem
  2546 {
  2547 public:
  2548   int menu_bar_id;
  2549   void *menu_ptr;
  2550   void *wind_ptr;
  2551   char *key;
  2552   char *help;
  2553 
  2554   EmacsMenuItem (const char *key_label, const char *label,
  2555                  const char *help, BMessage *message = NULL)
  2556     : BMenuItem (label, message),
  2557       menu_bar_id (-1),
  2558       menu_ptr (NULL),
  2559       wind_ptr (NULL),
  2560       key (NULL),
  2561       help (NULL)
  2562   {
  2563     if (key_label)
  2564       key = strdup (key_label);
  2565 
  2566     if (help)
  2567       this->help = strdup (help);
  2568   }
  2569 
  2570   ~EmacsMenuItem ()
  2571   {
  2572     if (key)
  2573       free (key);
  2574     if (help)
  2575       free (help);
  2576   }
  2577 
  2578   void
  2579   DrawContent (void)
  2580   {
  2581     BMenu *menu = Menu ();
  2582 
  2583     BMenuItem::DrawContent ();
  2584 
  2585     if (key)
  2586       {
  2587         BRect r = Frame ();
  2588         int w;
  2589 
  2590         menu->PushState ();
  2591         menu->ClipToRect (r);
  2592         menu->SetFont (be_plain_font);
  2593         w = menu->StringWidth (key);
  2594         menu->MovePenTo (BPoint (BE_RECT_WIDTH (r) - w - 4,
  2595                                  menu->PenLocation ().y));
  2596         menu->DrawString (key);
  2597         menu->PopState ();
  2598       }
  2599   }
  2600 
  2601   void
  2602   GetContentSize (float *w, float *h)
  2603   {
  2604     BMenuItem::GetContentSize (w, h);
  2605     if (Menu () && key)
  2606       *w += 4 + Menu ()->StringWidth (key);
  2607   }
  2608 
  2609   void
  2610   Highlight (bool highlight_p)
  2611   {
  2612     struct haiku_menu_bar_help_event rq;
  2613     struct haiku_dummy_event dummy;
  2614     BMenu *menu = Menu ();
  2615     BRect r;
  2616     BPoint pt;
  2617     uint32 buttons;
  2618 
  2619     if (help)
  2620       menu->SetToolTip (highlight_p ? help : NULL);
  2621     else
  2622       {
  2623         rq.window = wind_ptr;
  2624         rq.mb_idx = highlight_p ? menu_bar_id : -1;
  2625         rq.highlight_p = highlight_p;
  2626         rq.data = menu_ptr;
  2627 
  2628         r = Frame ();
  2629         menu->GetMouse (&pt, &buttons);
  2630 
  2631         if (!highlight_p || r.Contains (pt))
  2632           {
  2633             if (menu_bar_id > 0)
  2634               haiku_write (MENU_BAR_HELP_EVENT, &rq);
  2635             else
  2636               {
  2637                 haiku_write_without_signal (MENU_BAR_HELP_EVENT, &rq, true);
  2638                 haiku_write (DUMMY_EVENT, &dummy);
  2639               }
  2640           }
  2641       }
  2642 
  2643     BMenuItem::Highlight (highlight_p);
  2644   }
  2645 };
  2646 
  2647 class EmacsFontPreviewDialog : public BWindow
  2648 {
  2649   BStringView text_view;
  2650   BMessenger preview_source;
  2651   BFont *current_font;
  2652   bool is_visible;
  2653 
  2654   void
  2655   DoLayout (void)
  2656   {
  2657     float width, height;
  2658 
  2659     text_view.GetPreferredSize (&width, &height);
  2660     text_view.ResizeTo (width - 1, height - 1);
  2661 
  2662     SetSizeLimits (width, width, height, height);
  2663     ResizeTo (width - 1, height - 1);
  2664   }
  2665 
  2666   bool
  2667   QuitRequested (void)
  2668   {
  2669     preview_source.SendMessage (QUIT_PREVIEW_DIALOG);
  2670 
  2671     return false;
  2672   }
  2673 
  2674   void
  2675   MessageReceived (BMessage *message)
  2676   {
  2677     int32 family, style;
  2678     uint32 flags;
  2679     font_family name;
  2680     font_style sname;
  2681     status_t rc;
  2682     const char *size_name;
  2683     int size;
  2684 
  2685     if (message->what == SET_FONT_INDICES)
  2686       {
  2687         size_name = message->FindString ("emacs:size");
  2688 
  2689         if (message->FindInt32 ("emacs:family", &family) != B_OK
  2690             || message->FindInt32 ("emacs:style", &style) != B_OK)
  2691           return;
  2692 
  2693         rc = get_font_family (family, &name, &flags);
  2694 
  2695         if (rc != B_OK)
  2696           return;
  2697 
  2698         rc = get_font_style (name, style, &sname, &flags);
  2699 
  2700         if (rc != B_OK)
  2701           return;
  2702 
  2703         if (current_font)
  2704           delete current_font;
  2705 
  2706         current_font = new BFont;
  2707         current_font->SetFamilyAndStyle (name, sname);
  2708 
  2709         if (message->GetBool ("emacs:disable_antialiasing", false))
  2710           current_font->SetFlags (B_DISABLE_ANTIALIASING);
  2711 
  2712         if (size_name && strlen (size_name))
  2713           {
  2714             size = atoi (size_name);
  2715             current_font->SetSize (size);
  2716           }
  2717 
  2718         text_view.SetFont (current_font);
  2719         DoLayout ();
  2720         return;
  2721       }
  2722 
  2723     BWindow::MessageReceived (message);
  2724   }
  2725 
  2726 public:
  2727 
  2728   EmacsFontPreviewDialog (BWindow *target)
  2729     : BWindow (BRect (45, 45, 500, 300),
  2730                "Preview font",
  2731                B_FLOATING_WINDOW_LOOK,
  2732                B_MODAL_APP_WINDOW_FEEL,
  2733                B_NOT_ZOOMABLE | B_NOT_RESIZABLE),
  2734       text_view (BRect (0, 0, 0, 0),
  2735                  NULL, "The quick brown fox "
  2736                  "jumped over the lazy dog"),
  2737       preview_source (target),
  2738       current_font (NULL)
  2739   {
  2740     AddChild (&text_view);
  2741     DoLayout ();
  2742   }
  2743 
  2744   ~EmacsFontPreviewDialog (void)
  2745   {
  2746     text_view.RemoveSelf ();
  2747 
  2748     if (current_font)
  2749       delete current_font;
  2750   }
  2751 };
  2752 
  2753 class TripleLayoutView : public BView
  2754 {
  2755   BScrollView *view_1;
  2756   BView *view_2, *view_3;
  2757 
  2758   void
  2759   FrameResized (float new_width, float new_height)
  2760   {
  2761     BRect frame;
  2762     float width, height, height_1, width_1;
  2763     float basic_height;
  2764 
  2765     frame = Frame ();
  2766 
  2767     view_2->GetPreferredSize (&width, &height);
  2768     view_3->GetPreferredSize (&width_1, &height_1);
  2769 
  2770     basic_height = height + height_1;
  2771 
  2772     view_1->MoveTo (0, 0);
  2773     view_1->ResizeTo (BE_RECT_WIDTH (frame),
  2774                       BE_RECT_HEIGHT (frame) - basic_height);
  2775     view_2->MoveTo (2, BE_RECT_HEIGHT (frame) - basic_height);
  2776     view_2->ResizeTo (BE_RECT_WIDTH (frame) - 4, height);
  2777     view_3->MoveTo (2, BE_RECT_HEIGHT (frame) - height_1);
  2778     view_3->ResizeTo (BE_RECT_WIDTH (frame) - 4, height_1);
  2779 
  2780     BView::FrameResized (new_width, new_height);
  2781   }
  2782 
  2783   /* This is called by the BSplitView.  */
  2784   BSize
  2785   MinSize (void)
  2786   {
  2787     float width, height;
  2788     float width_1, height_1;
  2789     BSize size_1;
  2790 
  2791     size_1 = view_1->MinSize ();
  2792     view_2->GetPreferredSize (&width, &height);
  2793     view_3->GetPreferredSize (&width_1, &height_1);
  2794 
  2795     return BSize (std::max (size_1.width,
  2796                             std::max (width_1, width)),
  2797                   std::max (size_1.height, height + height_1));
  2798   }
  2799 
  2800 public:
  2801   TripleLayoutView (BScrollView *first, BView *second,
  2802                     BView *third) : BView (NULL, B_FRAME_EVENTS),
  2803                                     view_1 (first),
  2804                                     view_2 (second),
  2805                                     view_3 (third)
  2806   {
  2807     FrameResized (801, 801);
  2808   }
  2809 };
  2810 
  2811 class EmacsFontSelectionDialog : public BWindow
  2812 {
  2813   BView basic_view;
  2814   BCheckBox antialias_checkbox;
  2815   BCheckBox preview_checkbox;
  2816   BSplitView split_view;
  2817   BListView font_family_pane;
  2818   BListView font_style_pane;
  2819   BScrollView font_family_scroller;
  2820   BScrollView font_style_scroller;
  2821   TripleLayoutView style_view;
  2822   BObjectList<BStringItem> all_families;
  2823   BObjectList<BStringItem> all_styles;
  2824   BButton cancel_button, ok_button;
  2825   BTextControl size_entry;
  2826   port_id comm_port;
  2827   bool allow_monospace_only;
  2828   int pending_selection_idx;
  2829   EmacsFontPreviewDialog *preview;
  2830 
  2831   void
  2832   ShowPreview (void)
  2833   {
  2834     if (!preview)
  2835       {
  2836         preview = new EmacsFontPreviewDialog (this);
  2837         preview->Show ();
  2838 
  2839         UpdatePreview ();
  2840       }
  2841   }
  2842 
  2843   void
  2844   UpdatePreview (void)
  2845   {
  2846     int family, style;
  2847     BMessage message;
  2848     BMessenger messenger (preview);
  2849 
  2850     family = font_family_pane.CurrentSelection ();
  2851     style = font_style_pane.CurrentSelection ();
  2852 
  2853     message.what = SET_FONT_INDICES;
  2854     message.AddInt32 ("emacs:family", family);
  2855     message.AddInt32 ("emacs:style", style);
  2856 
  2857     if (antialias_checkbox.Value () == B_CONTROL_ON)
  2858       message.AddBool ("emacs:disable_antialiasing", true);
  2859 
  2860     message.AddString ("emacs:size",
  2861                        size_entry.Text ());
  2862 
  2863     messenger.SendMessage (&message);
  2864   }
  2865 
  2866   void
  2867   HidePreview (void)
  2868   {
  2869     if (preview)
  2870       {
  2871         if (preview->LockLooper ())
  2872           preview->Quit ();
  2873         /* I hope this works.  */
  2874         else
  2875           delete preview;
  2876 
  2877         preview = NULL;
  2878       }
  2879   }
  2880 
  2881   void
  2882   UpdateStylesForIndex (int idx)
  2883   {
  2884     int n, i, previous_selection;
  2885     uint32 flags;
  2886     font_family family;
  2887     font_style style;
  2888     BStringItem *item;
  2889     char *current_style;
  2890 
  2891     n = all_styles.CountItems ();
  2892     current_style = NULL;
  2893     previous_selection = font_style_pane.CurrentSelection ();
  2894 
  2895     if (previous_selection >= 0)
  2896       {
  2897         item = all_styles.ItemAt (previous_selection);
  2898         current_style = strdup (item->Text ());
  2899       }
  2900 
  2901     font_style_pane.MakeEmpty ();
  2902     all_styles.MakeEmpty ();
  2903 
  2904     if (get_font_family (idx, &family, &flags) == B_OK)
  2905       {
  2906         n = count_font_styles (family);
  2907 
  2908         for (i = 0; i < n; ++i)
  2909           {
  2910             if (get_font_style (family, i, &style, &flags) == B_OK)
  2911               item = new BStringItem (style);
  2912             else
  2913               item = new BStringItem ("<error>");
  2914 
  2915             if (current_style && pending_selection_idx < 0
  2916                 && !strcmp (current_style, style))
  2917               pending_selection_idx = i;
  2918 
  2919             font_style_pane.AddItem (item);
  2920             all_styles.AddItem (item);
  2921           }
  2922       }
  2923 
  2924     if (pending_selection_idx >= 0)
  2925       {
  2926         font_style_pane.Select (pending_selection_idx);
  2927         font_style_pane.ScrollToSelection ();
  2928       }
  2929 
  2930     pending_selection_idx = -1;
  2931     UpdateForSelectedStyle ();
  2932 
  2933     if (current_style)
  2934       free (current_style);
  2935   }
  2936 
  2937   bool
  2938   QuitRequested (void)
  2939   {
  2940     struct font_selection_dialog_message rq;
  2941 
  2942     rq.cancel = true;
  2943     write_port (comm_port, 0, &rq, sizeof rq);
  2944 
  2945     return false;
  2946   }
  2947 
  2948   void
  2949   UpdateForSelectedStyle (void)
  2950   {
  2951     int style = font_style_pane.CurrentSelection ();
  2952 
  2953     if (style < 0)
  2954       ok_button.SetEnabled (false);
  2955     else
  2956       ok_button.SetEnabled (true);
  2957 
  2958     if (style >= 0 && preview)
  2959       UpdatePreview ();
  2960   }
  2961 
  2962   void
  2963   MessageReceived (BMessage *msg)
  2964   {
  2965     const char *text;
  2966     int idx;
  2967     struct font_selection_dialog_message rq;
  2968 
  2969     if (msg->what == FONT_FAMILY_SELECTED)
  2970       {
  2971         idx = font_family_pane.CurrentSelection ();
  2972         UpdateStylesForIndex (idx);
  2973       }
  2974     else if (msg->what == FONT_STYLE_SELECTED)
  2975       UpdateForSelectedStyle ();
  2976     else if (msg->what == B_OK
  2977              && font_style_pane.CurrentSelection () >= 0)
  2978       {
  2979         text = size_entry.Text ();
  2980 
  2981         rq.cancel = false;
  2982         rq.family_idx = font_family_pane.CurrentSelection ();
  2983         rq.style_idx = font_style_pane.CurrentSelection ();
  2984         rq.size = atoi (text);
  2985         rq.size_specified = rq.size > 0 || strlen (text);
  2986 
  2987         if (antialias_checkbox.Value () == B_CONTROL_ON)
  2988           rq.disable_antialias = true;
  2989         else
  2990           rq.disable_antialias = false;
  2991 
  2992         write_port (comm_port, 0, &rq, sizeof rq);
  2993       }
  2994     else if (msg->what == B_CANCEL)
  2995       {
  2996         rq.cancel = true;
  2997 
  2998         write_port (comm_port, 0, &rq, sizeof rq);
  2999       }
  3000     else if (msg->what == SET_PREVIEW_DIALOG)
  3001       {
  3002         if (preview_checkbox.Value () == B_CONTROL_OFF)
  3003           HidePreview ();
  3004         else
  3005           ShowPreview ();
  3006       }
  3007     else if (msg->what == QUIT_PREVIEW_DIALOG)
  3008       {
  3009         preview_checkbox.SetValue (B_CONTROL_OFF);
  3010         HidePreview ();
  3011       }
  3012     else if (msg->what == UPDATE_PREVIEW_DIALOG)
  3013       {
  3014         if (preview)
  3015           UpdatePreview ();
  3016       }
  3017     else if (msg->what == SET_DISABLE_ANTIALIASING)
  3018       {
  3019         if (preview)
  3020           UpdatePreview ();
  3021       }
  3022 
  3023     BWindow::MessageReceived (msg);
  3024   }
  3025 
  3026 public:
  3027 
  3028   ~EmacsFontSelectionDialog (void)
  3029   {
  3030     if (preview)
  3031       {
  3032         if (preview->LockLooper ())
  3033           preview->Quit ();
  3034         /* I hope this works.  */
  3035         else
  3036           delete preview;
  3037       }
  3038 
  3039     font_family_pane.MakeEmpty ();
  3040     font_style_pane.MakeEmpty ();
  3041 
  3042     font_family_pane.RemoveSelf ();
  3043     font_style_pane.RemoveSelf ();
  3044     antialias_checkbox.RemoveSelf ();
  3045     preview_checkbox.RemoveSelf ();
  3046     style_view.RemoveSelf ();
  3047     font_family_scroller.RemoveSelf ();
  3048     font_style_scroller.RemoveSelf ();
  3049     cancel_button.RemoveSelf ();
  3050     ok_button.RemoveSelf ();
  3051     size_entry.RemoveSelf ();
  3052     basic_view.RemoveSelf ();
  3053 
  3054     if (comm_port >= B_OK)
  3055       delete_port (comm_port);
  3056   }
  3057 
  3058   EmacsFontSelectionDialog (bool monospace_only,
  3059                             int initial_family_idx,
  3060                             int initial_style_idx,
  3061                             int initial_size,
  3062                             bool initial_antialias)
  3063     : BWindow (BRect (0, 0, 500, 500),
  3064                "Select font from list",
  3065                B_TITLED_WINDOW_LOOK,
  3066                B_MODAL_APP_WINDOW_FEEL, 0),
  3067       basic_view (NULL, 0),
  3068       antialias_checkbox ("Disable antialiasing", "Disable antialiasing",
  3069                           new BMessage (SET_DISABLE_ANTIALIASING)),
  3070       preview_checkbox ("Show preview", "Show preview",
  3071                         new BMessage (SET_PREVIEW_DIALOG)),
  3072       font_family_pane (BRect (0, 0, 0, 0), NULL,
  3073                         B_SINGLE_SELECTION_LIST,
  3074                         B_FOLLOW_ALL_SIDES),
  3075       font_style_pane (BRect (0, 0, 0, 0), NULL,
  3076                        B_SINGLE_SELECTION_LIST,
  3077                        B_FOLLOW_ALL_SIDES),
  3078       font_family_scroller (NULL, &font_family_pane,
  3079                             B_FOLLOW_LEFT | B_FOLLOW_TOP,
  3080                             0, false, true),
  3081       font_style_scroller (NULL, &font_style_pane,
  3082                            B_FOLLOW_ALL_SIDES,
  3083                            B_SUPPORTS_LAYOUT, false, true),
  3084       style_view (&font_style_scroller, &antialias_checkbox,
  3085                   &preview_checkbox),
  3086       all_families (20, true),
  3087       all_styles (20, true),
  3088       cancel_button ("Cancel", "Cancel",
  3089                      new BMessage (B_CANCEL)),
  3090       ok_button ("OK", "OK", new BMessage (B_OK)),
  3091       size_entry (NULL, "Size:", NULL,
  3092                   new BMessage (UPDATE_PREVIEW_DIALOG)),
  3093       allow_monospace_only (monospace_only),
  3094       pending_selection_idx (initial_style_idx),
  3095       preview (NULL)
  3096   {
  3097     BStringItem *family_item;
  3098     int i, n_families;
  3099     font_family name;
  3100     uint32 flags, c;
  3101     BMessage *selection;
  3102     BTextView *size_text;
  3103     char format_buffer[4];
  3104 
  3105     AddChild (&basic_view);
  3106 
  3107     basic_view.AddChild (&split_view);
  3108     basic_view.AddChild (&cancel_button);
  3109     basic_view.AddChild (&ok_button);
  3110     basic_view.AddChild (&size_entry);
  3111     split_view.AddChild (&font_family_scroller, 0.7);
  3112     split_view.AddChild (&style_view, 0.3);
  3113     style_view.AddChild (&font_style_scroller);
  3114     style_view.AddChild (&antialias_checkbox);
  3115     style_view.AddChild (&preview_checkbox);
  3116 
  3117     basic_view.SetViewUIColor (B_PANEL_BACKGROUND_COLOR);
  3118     style_view.SetViewUIColor (B_PANEL_BACKGROUND_COLOR);
  3119 
  3120     FrameResized (801, 801);
  3121     UpdateForSelectedStyle ();
  3122 
  3123     selection = new BMessage (FONT_FAMILY_SELECTED);
  3124     font_family_pane.SetSelectionMessage (selection);
  3125     selection = new BMessage (FONT_STYLE_SELECTED);
  3126     font_style_pane.SetSelectionMessage (selection);
  3127     selection = new BMessage (B_OK);
  3128     font_style_pane.SetInvocationMessage (selection);
  3129     selection = new BMessage (UPDATE_PREVIEW_DIALOG);
  3130     size_entry.SetModificationMessage (selection);
  3131 
  3132     comm_port = create_port (1, "font dialog port");
  3133 
  3134     n_families = count_font_families ();
  3135 
  3136     for (i = 0; i < n_families; ++i)
  3137       {
  3138         if (get_font_family (i, &name, &flags) == B_OK)
  3139           {
  3140             family_item = new BStringItem (name);
  3141 
  3142             all_families.AddItem (family_item);
  3143             font_family_pane.AddItem (family_item);
  3144 
  3145             family_item->SetEnabled (!allow_monospace_only
  3146                                      || flags & B_IS_FIXED);
  3147           }
  3148         else
  3149           {
  3150             family_item = new BStringItem ("<error>");
  3151 
  3152             all_families.AddItem (family_item);
  3153             font_family_pane.AddItem (family_item);
  3154           }
  3155       }
  3156 
  3157     if (initial_family_idx >= 0)
  3158       {
  3159         font_family_pane.Select (initial_family_idx);
  3160         font_family_pane.ScrollToSelection ();
  3161       }
  3162 
  3163     size_text = size_entry.TextView ();
  3164 
  3165     for (c = 0; c <= 47; ++c)
  3166       size_text->DisallowChar (c);
  3167 
  3168     for (c = 58; c <= 127; ++c)
  3169       size_text->DisallowChar (c);
  3170 
  3171     if (initial_size > 0 && initial_size < 1000)
  3172       {
  3173         sprintf (format_buffer, "%d", initial_size);
  3174         size_entry.SetText (format_buffer);
  3175       }
  3176 
  3177     if (!initial_antialias)
  3178       antialias_checkbox.SetValue (B_CONTROL_ON);
  3179   }
  3180 
  3181   void
  3182   FrameResized (float new_width, float new_height)
  3183   {
  3184     BRect frame;
  3185     float ok_height, ok_width;
  3186     float cancel_height, cancel_width;
  3187     float size_width, size_height;
  3188     float bone;
  3189     int max_height;
  3190 
  3191     ok_button.GetPreferredSize (&ok_width, &ok_height);
  3192     cancel_button.GetPreferredSize (&cancel_width,
  3193                                     &cancel_height);
  3194     size_entry.GetPreferredSize (&size_width, &size_height);
  3195 
  3196     max_height = std::max (std::max (ok_height, cancel_height),
  3197                            size_height);
  3198 
  3199     SetSizeLimits (cancel_width + ok_width + size_width + 6,
  3200                    65535, max_height + 64, 65535);
  3201     frame = Frame ();
  3202 
  3203     basic_view.ResizeTo (BE_RECT_WIDTH (frame), BE_RECT_HEIGHT (frame));
  3204     split_view.ResizeTo (BE_RECT_WIDTH (frame) - 1,
  3205                          BE_RECT_HEIGHT (frame) - 4 - max_height);
  3206 
  3207     bone = BE_RECT_HEIGHT (frame) - 2 - max_height / 2;
  3208 
  3209     ok_button.MoveTo ((BE_RECT_WIDTH (frame)
  3210                        - 4 - cancel_width - ok_width),
  3211                       bone - ok_height / 2);
  3212     cancel_button.MoveTo (BE_RECT_WIDTH (frame) - 2 - cancel_width,
  3213                           bone - cancel_height / 2);
  3214     size_entry.MoveTo (2, bone - size_height / 2);
  3215 
  3216     ok_button.ResizeTo (ok_width, ok_height);
  3217     cancel_button.ResizeTo (cancel_width, cancel_height);
  3218     size_entry.ResizeTo (std::max (size_width,
  3219                                    BE_RECT_WIDTH (frame) / 4),
  3220                          size_height);
  3221   }
  3222 
  3223   void
  3224   WaitForChoice (struct font_selection_dialog_message *msg,
  3225                  void (*process_pending_signals_function) (void),
  3226                  bool (*should_quit_function) (void))
  3227   {
  3228     int32 reply_type;
  3229     struct object_wait_info infos[2];
  3230     ssize_t status;
  3231 
  3232     infos[0].object = port_application_to_emacs;
  3233     infos[0].type = B_OBJECT_TYPE_PORT;
  3234     infos[0].events = B_EVENT_READ;
  3235 
  3236     infos[1].object = comm_port;
  3237     infos[1].type = B_OBJECT_TYPE_PORT;
  3238     infos[1].events = B_EVENT_READ;
  3239 
  3240     while (true)
  3241       {
  3242         status = wait_for_objects (infos, 2);
  3243 
  3244         if (status < B_OK)
  3245           continue;
  3246 
  3247         if (infos[1].events & B_EVENT_READ)
  3248           {
  3249             if (read_port (comm_port, &reply_type,
  3250                            msg, sizeof *msg) >= B_OK)
  3251               return;
  3252 
  3253             goto cancel;
  3254           }
  3255 
  3256         if (infos[0].events & B_EVENT_READ)
  3257           process_pending_signals_function ();
  3258 
  3259         if (should_quit_function ())
  3260           goto cancel;
  3261 
  3262         infos[0].events = B_EVENT_READ;
  3263         infos[1].events = B_EVENT_READ;
  3264       }
  3265 
  3266   cancel:
  3267     msg->cancel = true;
  3268     return;
  3269   }
  3270 
  3271   status_t
  3272   InitCheck (void)
  3273   {
  3274     return comm_port >= B_OK ? B_OK : comm_port;
  3275   }
  3276 };
  3277 
  3278 class EmacsFilePanelCallbackLooper : public BLooper
  3279 {
  3280   port_id comm_port;
  3281 
  3282   void
  3283   MessageReceived (BMessage *msg)
  3284   {
  3285     const char *str_path, *name;
  3286     char *file_name, *str_buf;
  3287     BEntry entry;
  3288     BPath path;
  3289     entry_ref ref;
  3290     int32 old_what;
  3291 
  3292     if (msg->what == FILE_PANEL_SELECTION
  3293         || ((msg->FindInt32 ("old_what", &old_what) == B_OK
  3294              && old_what == FILE_PANEL_SELECTION)))
  3295       {
  3296         file_name = NULL;
  3297 
  3298         if (msg->FindRef ("refs", &ref) == B_OK
  3299             && entry.SetTo (&ref, 0) == B_OK
  3300             && entry.GetPath (&path) == B_OK)
  3301           {
  3302             str_path = path.Path ();
  3303 
  3304             if (str_path)
  3305               file_name = strdup (str_path);
  3306           }
  3307         else if (msg->FindRef ("directory", &ref) == B_OK
  3308                  && entry.SetTo (&ref, 0) == B_OK
  3309                  && entry.GetPath (&path) == B_OK)
  3310           {
  3311             name = msg->GetString ("name");
  3312             str_path = path.Path ();
  3313 
  3314             if (name)
  3315               {
  3316                 str_buf = (char *) alloca (std::strlen (str_path)
  3317                                            + std::strlen (name) + 2);
  3318                 sprintf (str_buf, "%s/%s", str_path, name);
  3319                 file_name = strdup (str_buf);
  3320               }
  3321           }
  3322 
  3323         write_port (comm_port, 0, &file_name, sizeof file_name);
  3324       }
  3325 
  3326     BLooper::MessageReceived (msg);
  3327   }
  3328 
  3329 public:
  3330   EmacsFilePanelCallbackLooper (void) : BLooper ()
  3331   {
  3332     comm_port = create_port (1, "file panel port");
  3333   }
  3334 
  3335   ~EmacsFilePanelCallbackLooper (void)
  3336   {
  3337     delete_port (comm_port);
  3338   }
  3339 
  3340   char *
  3341   ReadFileName (void (*process_pending_signals_function) (void))
  3342   {
  3343     object_wait_info infos[2];
  3344     ssize_t status;
  3345     int32 reply_type;
  3346     char *file_name;
  3347 
  3348     file_name = NULL;
  3349 
  3350     infos[0].object = port_application_to_emacs;
  3351     infos[0].type = B_OBJECT_TYPE_PORT;
  3352     infos[0].events = B_EVENT_READ;
  3353 
  3354     infos[1].object = comm_port;
  3355     infos[1].type = B_OBJECT_TYPE_PORT;
  3356     infos[1].events = B_EVENT_READ;
  3357 
  3358     while (true)
  3359       {
  3360         status = wait_for_objects (infos, 2);
  3361 
  3362         if (status == B_INTERRUPTED || status == B_WOULD_BLOCK)
  3363           continue;
  3364 
  3365         if (infos[0].events & B_EVENT_READ)
  3366           process_pending_signals_function ();
  3367 
  3368         if (infos[1].events & B_EVENT_READ)
  3369           {
  3370             status = read_port (comm_port,
  3371                                 &reply_type, &file_name,
  3372                                 sizeof file_name);
  3373 
  3374             if (status < B_OK)
  3375               file_name = NULL;
  3376 
  3377             goto out;
  3378           }
  3379 
  3380         infos[0].events = B_EVENT_READ;
  3381         infos[1].events = B_EVENT_READ;
  3382       }
  3383 
  3384   out:
  3385     return file_name;
  3386   }
  3387 
  3388   status_t
  3389   InitCheck (void)
  3390   {
  3391     return comm_port >= B_OK ? B_OK : comm_port;
  3392   }
  3393 };
  3394 
  3395 /* A view that is added as a child of a tooltip's text view, and
  3396    prevents motion events from reaching it (thereby moving the
  3397    tooltip).  */
  3398 class EmacsMotionSuppressionView : public BView
  3399 {
  3400   void
  3401   AttachedToWindow (void)
  3402   {
  3403     BView *text_view, *tooltip_view;
  3404 
  3405     /* We know that this view is a child of the text view, whose
  3406        parent is the tooltip view, and that the tooltip view has
  3407        already set its mouse event mask.  */
  3408 
  3409     text_view = Parent ();
  3410 
  3411     if (!text_view)
  3412       return;
  3413 
  3414     tooltip_view = text_view->Parent ();
  3415 
  3416     if (!tooltip_view)
  3417       return;
  3418 
  3419     tooltip_view->SetEventMask (B_KEYBOARD_EVENTS, 0);
  3420   }
  3421 
  3422 public:
  3423   EmacsMotionSuppressionView (void) : BView (BRect (-1, -1, 1, 1),
  3424                                              NULL, 0, 0)
  3425   {
  3426     return;
  3427   }
  3428 };
  3429 
  3430 static int32
  3431 start_running_application (void *data)
  3432 {
  3433   Emacs *app = (Emacs *) data;
  3434 
  3435   haiku_io_init_in_app_thread ();
  3436 
  3437   if (!app->Lock ())
  3438     gui_abort ("Failed to lock application");
  3439 
  3440   app->Run ();
  3441   app->Unlock ();
  3442   return 0;
  3443 }
  3444 
  3445 /* Take BITMAP, a reference to a BBitmap, and return a pointer to its
  3446    data.  */
  3447 void *
  3448 BBitmap_data (void *bitmap)
  3449 {
  3450   return ((BBitmap *) bitmap)->Bits ();
  3451 }
  3452 
  3453 /* Convert bitmap if required, placing the new bitmap in NEW_BITMAP,
  3454    and return non-null if bitmap was successfully converted.  Bitmaps
  3455    should be freed with `BBitmap_free'.  */
  3456 int
  3457 BBitmap_convert (void *_bitmap, void **new_bitmap)
  3458 {
  3459   BBitmap *bitmap = (BBitmap *) _bitmap;
  3460   if (bitmap->ColorSpace () == B_RGBA32)
  3461     return -1;
  3462   BRect bounds = bitmap->Bounds ();
  3463   BBitmap *bmp = new (std::nothrow) BBitmap (bounds, B_RGBA32);
  3464   if (!bmp || bmp->InitCheck () != B_OK)
  3465     {
  3466       if (bmp)
  3467         delete bmp;
  3468       return 0;
  3469     }
  3470   if (bmp->ImportBits (bitmap) != B_OK)
  3471     {
  3472       delete bmp;
  3473       return 0;
  3474     }
  3475   *(BBitmap **) new_bitmap = bmp;
  3476   return 1;
  3477 }
  3478 
  3479 void
  3480 BBitmap_free (void *bitmap)
  3481 {
  3482   delete (BBitmap *) bitmap;
  3483 }
  3484 
  3485 /* Create new bitmap in RGB32 format, or in GRAY1 if MONO_P is
  3486    non-zero.  */
  3487 void *
  3488 BBitmap_new (int width, int height, int mono_p)
  3489 {
  3490   BBitmap *bn = new (std::nothrow) BBitmap (BRect (0, 0, width - 1, height - 1),
  3491                                             mono_p ? B_GRAY1 : B_RGB32);
  3492 
  3493   return bn->InitCheck () == B_OK ? (void *) bn : (void *) (delete bn, NULL);
  3494 }
  3495 
  3496 void
  3497 BBitmap_dimensions (void *bitmap, int *left, int *top,
  3498                     int *right, int *bottom,
  3499                     int32_t *bytes_per_row, int *mono_p)
  3500 {
  3501   BRect rect = ((BBitmap *) bitmap)->Bounds ();
  3502   *left = rect.left;
  3503   *top = rect.top;
  3504   *right = rect.right;
  3505   *bottom = rect.bottom;
  3506 
  3507   *bytes_per_row = ((BBitmap *) bitmap)->BytesPerRow ();
  3508   *mono_p = (((BBitmap *) bitmap)->ColorSpace () == B_GRAY1);
  3509 }
  3510 
  3511 static void
  3512 wait_for_exit_of_app_thread (void)
  3513 {
  3514   status_t ret;
  3515 
  3516   be_app->PostMessage (QUIT_APPLICATION);
  3517   wait_for_thread (app_thread, &ret);
  3518 }
  3519 
  3520 /* Set up an application and return it.  If starting the application
  3521    thread fails, abort Emacs.  */
  3522 void *
  3523 BApplication_setup (void)
  3524 {
  3525   thread_id id;
  3526   Emacs *app;
  3527 
  3528   if (be_app)
  3529     return be_app;
  3530 
  3531   app = new Emacs;
  3532   app->Unlock ();
  3533 
  3534   if ((id = spawn_thread (start_running_application, "Emacs app thread",
  3535                           B_DEFAULT_MEDIA_PRIORITY, app)) < 0)
  3536     gui_abort ("spawn_thread failed");
  3537 
  3538   resume_thread (id);
  3539   app_thread = id;
  3540 
  3541   atexit (wait_for_exit_of_app_thread);
  3542   return app;
  3543 }
  3544 
  3545 /* Set up and return a window with its view put in VIEW.  */
  3546 void *
  3547 BWindow_new (void **view)
  3548 {
  3549   BWindow *window;
  3550   BView *vw;
  3551 
  3552   window = new (std::nothrow) EmacsWindow;
  3553   if (!window)
  3554     {
  3555       *view = NULL;
  3556       return window;
  3557     }
  3558 
  3559   vw = new (std::nothrow) EmacsView;
  3560   if (!vw)
  3561     {
  3562       *view = NULL;
  3563       window->LockLooper ();
  3564       window->Quit ();
  3565       return NULL;
  3566     }
  3567 
  3568   /* Windows are created locked by the current thread, but calling
  3569      Show for the first time causes them to be unlocked.  To avoid a
  3570      deadlock when a frame is created invisible in one thread, and
  3571      another thread later tries to lock it, the window is unlocked
  3572      here, and EmacsShow will lock it manually if it's being shown for
  3573      the first time.  */
  3574   window->UnlockLooper ();
  3575   window->AddChild (vw);
  3576   *view = vw;
  3577   return window;
  3578 }
  3579 
  3580 void
  3581 BWindow_quit (void *window)
  3582 {
  3583   BWindow *w = (BWindow *) window;
  3584 
  3585   w->LockLooper ();
  3586   w->Quit ();
  3587 }
  3588 
  3589 /* Set WINDOW's offset to X, Y.  */
  3590 void
  3591 BWindow_set_offset (void *window, int x, int y)
  3592 {
  3593   BWindow *wn = (BWindow *) window;
  3594   EmacsWindow *w = dynamic_cast<EmacsWindow *> (wn);
  3595   if (w)
  3596     {
  3597       if (!w->LockLooper ())
  3598         gui_abort ("Failed to lock window looper setting offset");
  3599       w->EmacsMoveTo (x, y);
  3600       w->UnlockLooper ();
  3601     }
  3602   else
  3603     wn->MoveTo (x, y);
  3604 }
  3605 
  3606 void
  3607 BWindow_dimensions (void *window, int *width, int *height)
  3608 {
  3609   BWindow *w = (BWindow *) window;
  3610   BRect frame = w->Frame ();
  3611 
  3612   *width = BE_RECT_WIDTH (frame);
  3613   *height = BE_RECT_HEIGHT (frame);
  3614 }
  3615 
  3616 /* Iconify WINDOW.  */
  3617 void
  3618 BWindow_iconify (void *window)
  3619 {
  3620   if (((BWindow *) window)->IsHidden ())
  3621     BWindow_set_visible (window, true);
  3622   ((BWindow *) window)->Minimize (true);
  3623 }
  3624 
  3625 /* Show or hide WINDOW.  */
  3626 void
  3627 BWindow_set_visible (void *window, int visible_p)
  3628 {
  3629   EmacsWindow *win = (EmacsWindow *) window;
  3630   if (visible_p)
  3631     {
  3632       if (win->IsMinimized ())
  3633         win->Minimize (false);
  3634       win->EmacsShow ();
  3635     }
  3636   else if (!win->IsHidden ())
  3637     {
  3638       if (win->IsMinimized ())
  3639         win->Minimize (false);
  3640       win->EmacsHide ();
  3641     }
  3642 }
  3643 
  3644 /* Change the title of WINDOW to the multibyte string TITLE.  */
  3645 void
  3646 BWindow_retitle (void *window, const char *title)
  3647 {
  3648   ((BWindow *) window)->SetTitle (title);
  3649 }
  3650 
  3651 /* Resize WINDOW to WIDTH by HEIGHT.  */
  3652 void
  3653 BWindow_resize (void *window, int width, int height)
  3654 {
  3655   ((BWindow *) window)->ResizeTo (width - 1, height - 1);
  3656 }
  3657 
  3658 /* Activate WINDOW, making it the subject of keyboard focus and
  3659    bringing it to the front of the screen.  */
  3660 void
  3661 BWindow_activate (void *window)
  3662 {
  3663   ((BWindow *) window)->Activate ();
  3664 }
  3665 
  3666 /* Return the pixel dimensions of the main screen in WIDTH and
  3667    HEIGHT.  */
  3668 void
  3669 be_get_screen_dimensions (int *width, int *height)
  3670 {
  3671   BScreen screen;
  3672   BRect frame;
  3673 
  3674   if (!screen.IsValid ())
  3675     gui_abort ("Invalid screen");
  3676 
  3677   frame = screen.Frame ();
  3678 
  3679   *width = BE_RECT_WIDTH (frame);
  3680   *height = BE_RECT_HEIGHT (frame);
  3681 }
  3682 
  3683 /* Resize VIEW to WIDTH, HEIGHT.  */
  3684 void
  3685 BView_resize_to (void *view, int width, int height)
  3686 {
  3687   EmacsView *vw = (EmacsView *) view;
  3688   if (!vw->LockLooper ())
  3689     gui_abort ("Failed to lock view for resize");
  3690   vw->ResizeTo (width, height);
  3691   vw->AfterResize ();
  3692   vw->UnlockLooper ();
  3693 }
  3694 
  3695 void
  3696 be_delete_cursor (void *cursor)
  3697 {
  3698   if (cursor)
  3699     delete (BCursor *) cursor;
  3700 }
  3701 
  3702 void *
  3703 be_create_cursor_from_id (int id)
  3704 {
  3705   return new BCursor ((enum BCursorID) id);
  3706 }
  3707 
  3708 void
  3709 BView_set_view_cursor (void *view, void *cursor)
  3710 {
  3711   BView *v = (BView *) view;
  3712 
  3713   if (!v->LockLooper ())
  3714     gui_abort ("Failed to lock view setting cursor");
  3715   v->SetViewCursor ((BCursor *) cursor);
  3716   v->UnlockLooper ();
  3717 }
  3718 
  3719 void
  3720 BWindow_Flush (void *window)
  3721 {
  3722   ((BWindow *) window)->Flush ();
  3723 }
  3724 
  3725 /* Make a scrollbar, attach it to VIEW's window, and return it.  */
  3726 void *
  3727 be_make_scroll_bar_for_view (void *view, int horizontal_p,
  3728                              int x, int y, int x1, int y1)
  3729 {
  3730   EmacsScrollBar *scroll_bar;
  3731   BView *vw = (BView *) view;
  3732 
  3733   if (!vw->LockLooper ())
  3734     gui_abort ("Failed to lock scrollbar owner");
  3735 
  3736   scroll_bar = new EmacsScrollBar (x, y, x1, y1, horizontal_p,
  3737                                    (EmacsView *) vw);
  3738 
  3739   vw->AddChild (scroll_bar);
  3740   vw->UnlockLooper ();
  3741 
  3742   return scroll_bar;
  3743 }
  3744 
  3745 void
  3746 BScrollBar_delete (void *sb)
  3747 {
  3748   BView *view = (BView *) sb;
  3749   BView *pr = view->Parent ();
  3750 
  3751   if (!pr->LockLooper ())
  3752     gui_abort ("Failed to lock scrollbar parent");
  3753   pr->RemoveChild (view);
  3754   pr->UnlockLooper ();
  3755 
  3756   delete (EmacsScrollBar *) sb;
  3757 }
  3758 
  3759 void
  3760 BView_move_frame (void *view, int x, int y, int x1, int y1)
  3761 {
  3762   BView *vw = (BView *) view;
  3763 
  3764   if (!vw->LockLooper ())
  3765     gui_abort ("Failed to lock view moving frame");
  3766   vw->MoveTo (x, y);
  3767   vw->ResizeTo (x1 - x, y1 - y);
  3768   vw->UnlockLooper ();
  3769 }
  3770 
  3771 /* DRAGGING can either be 0 (which means to update everything), 1
  3772    (which means to update nothing), or -1 (which means to update only
  3773    the thumb size and range).  */
  3774 
  3775 void
  3776 BView_scroll_bar_update (void *sb, int portion, int whole, int position,
  3777                          int dragging, bool can_overscroll)
  3778 {
  3779   BScrollBar *bar = (BScrollBar *) sb;
  3780   BMessage msg = BMessage (SCROLL_BAR_UPDATE);
  3781   BMessenger mr = BMessenger (bar);
  3782   msg.AddInt32 ("emacs:range", whole);
  3783   msg.AddInt32 ("emacs:units", position);
  3784   msg.AddInt32 ("emacs:portion", portion);
  3785   msg.AddInt32 ("emacs:dragging", dragging);
  3786   msg.AddBool ("emacs:overscroll", can_overscroll);
  3787 
  3788   mr.SendMessage (&msg);
  3789 }
  3790 
  3791 /* Return the default scrollbar size.  */
  3792 int
  3793 BScrollBar_default_size (int horizontal_p)
  3794 {
  3795   return be_control_look->GetScrollBarWidth (horizontal_p
  3796                                              ? B_HORIZONTAL
  3797                                              : B_VERTICAL);
  3798 }
  3799 
  3800 /* Invalidate VIEW, causing it to be drawn again.  */
  3801 void
  3802 BView_invalidate (void *view)
  3803 {
  3804   BView *vw = (BView *) view;
  3805   if (!vw->LockLooper ())
  3806     gui_abort ("Couldn't lock view while invalidating it");
  3807   vw->Invalidate ();
  3808   vw->UnlockLooper ();
  3809 }
  3810 
  3811 /* Lock VIEW in preparation for drawing operations.  This should be
  3812    called before any attempt to draw onto VIEW or to lock it for Cairo
  3813    drawing.  `BView_draw_unlock' should be called afterwards.
  3814 
  3815    If any drawing is going to take place, INVALID_REGION should be
  3816    true, and X, Y, WIDTH, HEIGHT should specify a rectangle in which
  3817    the drawing will take place.  */
  3818 void
  3819 BView_draw_lock (void *view, bool invalidate_region,
  3820                  int x, int y, int width, int height)
  3821 {
  3822   EmacsView *vw = (EmacsView *) view;
  3823   if (vw->looper_locked_count)
  3824     {
  3825       vw->looper_locked_count++;
  3826 
  3827       if (invalidate_region && vw->offscreen_draw_view)
  3828         vw->invalid_region.Include (BRect (x, y, x + width - 1,
  3829                                            y + height - 1));
  3830       return;
  3831     }
  3832   BView *v = (BView *) find_appropriate_view_for_draw (vw);
  3833   if (v != vw)
  3834     {
  3835       if (!vw->offscreen_draw_bitmap_1->Lock ())
  3836         gui_abort ("Failed to lock offscreen bitmap while acquiring draw lock");
  3837     }
  3838   else if (!v->LockLooper ())
  3839     gui_abort ("Failed to lock draw view while acquiring draw lock");
  3840 
  3841   if (v != vw && !vw->LockLooper ())
  3842     gui_abort ("Failed to lock view while acquiring draw lock");
  3843 
  3844   if (invalidate_region && vw->offscreen_draw_view)
  3845     vw->invalid_region.Include (BRect (x, y, x + width - 1,
  3846                                        y + height - 1));
  3847   vw->looper_locked_count++;
  3848 }
  3849 
  3850 void
  3851 BView_invalidate_region (void *view, int x, int y, int width, int height)
  3852 {
  3853   EmacsView *vw = (EmacsView *) view;
  3854 
  3855   if (vw->offscreen_draw_view)
  3856     vw->invalid_region.Include (BRect (x, y, x + width - 1,
  3857                                        y + height - 1));
  3858 }
  3859 
  3860 void
  3861 BView_draw_unlock (void *view)
  3862 {
  3863   EmacsView *vw = (EmacsView *) view;
  3864   if (--vw->looper_locked_count)
  3865     return;
  3866 
  3867   BView *v = (BView *) find_appropriate_view_for_draw (view);
  3868   if (v == vw)
  3869     vw->UnlockLooper ();
  3870   else
  3871     {
  3872       vw->offscreen_draw_bitmap_1->Unlock ();
  3873       vw->UnlockLooper ();
  3874     }
  3875 }
  3876 
  3877 void
  3878 BWindow_center_on_screen (void *window)
  3879 {
  3880   BWindow *w = (BWindow *) window;
  3881   w->CenterOnScreen ();
  3882 }
  3883 
  3884 /* Import fringe bitmap (short array, low bit rightmost) BITS into
  3885    BITMAP using the B_GRAY1 colorspace.  */
  3886 void
  3887 BBitmap_import_fringe_bitmap (void *bitmap, unsigned short *bits, int wd, int h)
  3888 {
  3889   BBitmap *bmp = (BBitmap *) bitmap;
  3890   unsigned char *data = (unsigned char *) bmp->Bits ();
  3891   int i;
  3892 
  3893   for (i = 0; i < h; i++)
  3894     {
  3895       if (wd <= 8)
  3896         data[0] = bits[i] & 0xff;
  3897       else
  3898         {
  3899           data[1] = bits[i] & 0xff;
  3900           data[0] = bits[i] >> 8;
  3901         }
  3902 
  3903       data += bmp->BytesPerRow ();
  3904     }
  3905 }
  3906 
  3907 /* Make a scrollbar at X, Y known to the view VIEW.  */
  3908 void
  3909 BView_publish_scroll_bar (void *view, int x, int y, int width, int height)
  3910 {
  3911   EmacsView *vw = (EmacsView *) view;
  3912   if (vw->LockLooper ())
  3913     {
  3914       vw->sb_region.Include (BRect (x, y, x - 1 + width,
  3915                                     y - 1 + height));
  3916       vw->UnlockLooper ();
  3917     }
  3918 }
  3919 
  3920 void
  3921 BView_forget_scroll_bar (void *view, int x, int y, int width, int height)
  3922 {
  3923   EmacsView *vw = (EmacsView *) view;
  3924   if (vw->LockLooper ())
  3925     {
  3926       vw->sb_region.Exclude (BRect (x, y, x - 1 + width,
  3927                                     y - 1 + height));
  3928       vw->UnlockLooper ();
  3929     }
  3930 }
  3931 
  3932 bool
  3933 BView_inside_scroll_bar (void *view, int x, int y)
  3934 {
  3935   EmacsView *vw = (EmacsView *) view;
  3936   bool val;
  3937 
  3938   if (vw->LockLooper ())
  3939     {
  3940       val = vw->sb_region.Contains (BPoint (x, y));
  3941       vw->UnlockLooper ();
  3942     }
  3943   else
  3944     val = false;
  3945 
  3946   return val;
  3947 }
  3948 
  3949 void
  3950 BView_get_mouse (void *view, int *x, int *y)
  3951 {
  3952   BPoint l;
  3953   BView *vw = (BView *) view;
  3954   if (!vw->LockLooper ())
  3955     gui_abort ("Failed to lock view in BView_get_mouse");
  3956   vw->GetMouse (&l, NULL, 1);
  3957   vw->UnlockLooper ();
  3958 
  3959   *x = std::lrint (l.x);
  3960   *y = std::lrint (l.y);
  3961 }
  3962 
  3963 /* Perform an in-place conversion of X and Y from VIEW's coordinate
  3964    system to its screen's coordinate system.  */
  3965 void
  3966 BView_convert_to_screen (void *view, int *x, int *y)
  3967 {
  3968   BPoint l = BPoint (*x, *y);
  3969   BView *vw = (BView *) view;
  3970   if (!vw->LockLooper ())
  3971     gui_abort ("Failed to lock view in convert_to_screen");
  3972   vw->ConvertToScreen (&l);
  3973   vw->UnlockLooper ();
  3974 
  3975   *x = std::lrint (l.x);
  3976   *y = std::lrint (l.y);
  3977 }
  3978 
  3979 void
  3980 BView_convert_from_screen (void *view, int *x, int *y)
  3981 {
  3982   BPoint l = BPoint (*x, *y);
  3983   BView *vw = (BView *) view;
  3984   if (!vw->LockLooper ())
  3985     gui_abort ("Failed to lock view in convert_from_screen");
  3986   vw->ConvertFromScreen (&l);
  3987   vw->UnlockLooper ();
  3988 
  3989   *x = std::lrint (l.x);
  3990   *y = std::lrint (l.y);
  3991 }
  3992 
  3993 /* Decorate or undecorate WINDOW depending on DECORATE_P.  */
  3994 void
  3995 BWindow_change_decoration (void *window, int decorate_p)
  3996 {
  3997   EmacsWindow *w = (EmacsWindow *) window;
  3998   if (!w->LockLooper ())
  3999     gui_abort ("Failed to lock window while changing its decorations");
  4000 
  4001   if (!w->override_redirect_p)
  4002     {
  4003       if (decorate_p)
  4004         w->SetLook (B_TITLED_WINDOW_LOOK);
  4005       else
  4006         w->SetLook (B_NO_BORDER_WINDOW_LOOK);
  4007     }
  4008   else
  4009     {
  4010       if (decorate_p)
  4011         w->pre_override_redirect_look = B_TITLED_WINDOW_LOOK;
  4012       else
  4013         w->pre_override_redirect_look = B_NO_BORDER_WINDOW_LOOK;
  4014     }
  4015   w->UnlockLooper ();
  4016 }
  4017 
  4018 /* Decorate WINDOW appropriately for use as a tooltip.  */
  4019 void
  4020 BWindow_set_tooltip_decoration (void *window)
  4021 {
  4022   EmacsWindow *w = (EmacsWindow *) window;
  4023   if (!w->LockLooper ())
  4024     gui_abort ("Failed to lock window while setting ttip decoration");
  4025   w->tooltip_p = true;
  4026   w->RecomputeFeel ();
  4027   w->SetLook (B_BORDERED_WINDOW_LOOK);
  4028   w->SetFlags (B_NOT_ZOOMABLE
  4029                | B_NOT_MINIMIZABLE
  4030                | B_AVOID_FRONT
  4031                | B_AVOID_FOCUS);
  4032   w->UnlockLooper ();
  4033 }
  4034 
  4035 /* Set B_AVOID_FOCUS on WINDOW if AVOID_FOCUS_P is non-nil, or clear
  4036    it otherwise.  */
  4037 void
  4038 BWindow_set_avoid_focus (void *window, int avoid_focus_p)
  4039 {
  4040   BWindow *w = (BWindow *) window;
  4041   if (!w->LockLooper ())
  4042     gui_abort ("Failed to lock window while setting avoid focus");
  4043 
  4044   if (!avoid_focus_p)
  4045     w->SetFlags (w->Flags () & ~B_AVOID_FOCUS);
  4046   else
  4047     w->SetFlags (w->Flags () | B_AVOID_FOCUS);
  4048   w->UnlockLooper ();
  4049 }
  4050 
  4051 void
  4052 BView_emacs_delete (void *view)
  4053 {
  4054   EmacsView *vw = (EmacsView *) view;
  4055   if (!vw->LockLooper ())
  4056     gui_abort ("Failed to lock view while deleting it");
  4057   vw->RemoveSelf ();
  4058   delete vw;
  4059 }
  4060 
  4061 /* Create a popup menu.  */
  4062 void *
  4063 BPopUpMenu_new (const char *name)
  4064 {
  4065   BPopUpMenu *menu = new BPopUpMenu (name);
  4066 
  4067   menu->SetRadioMode (0);
  4068   return menu;
  4069 }
  4070 
  4071 /* Add a title item to MENU.  These items cannot be highlighted or
  4072    triggered, and their labels will display as bold text.  */
  4073 void
  4074 BMenu_add_title (void *menu, const char *text)
  4075 {
  4076   BMenu *be_menu = (BMenu *) menu;
  4077   EmacsTitleMenuItem *it;
  4078 
  4079   it = new EmacsTitleMenuItem (text);
  4080   be_menu->AddItem (it);
  4081 }
  4082 
  4083 /* Add an item to the menu MENU.  */
  4084 void
  4085 BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
  4086                 bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
  4087                 const char *help)
  4088 {
  4089   BMenu *m = (BMenu *) menu;
  4090   BMessage *msg;
  4091   if (ptr)
  4092     msg = new BMessage ();
  4093   EmacsMenuItem *it = new EmacsMenuItem (key, label, help, ptr ? msg : NULL);
  4094   it->SetTarget (m->Window ());
  4095   it->SetEnabled (enabled_p);
  4096   it->SetMarked (marked_p);
  4097   if (mbar_p)
  4098     {
  4099       it->menu_bar_id = (intptr_t) ptr;
  4100       it->wind_ptr = mbw_ptr;
  4101     }
  4102   it->menu_ptr = ptr;
  4103   if (ptr)
  4104     msg->AddPointer ("menuptr", ptr);
  4105   m->AddItem (it);
  4106 }
  4107 
  4108 /* Add a separator to the menu MENU.  */
  4109 void
  4110 BMenu_add_separator (void *menu)
  4111 {
  4112   BMenu *m = (BMenu *) menu;
  4113 
  4114   m->AddSeparatorItem ();
  4115 }
  4116 
  4117 /* Create a submenu and attach it to MENU.  */
  4118 void *
  4119 BMenu_new_submenu (void *menu, const char *label, bool enabled_p)
  4120 {
  4121   BMenu *m = (BMenu *) menu;
  4122   BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
  4123   mn->SetRadioMode (0);
  4124   BMenuItem *i = new BMenuItem (mn);
  4125   i->SetEnabled (enabled_p);
  4126   m->AddItem (i);
  4127   return mn;
  4128 }
  4129 
  4130 /* Create a submenu that notifies Emacs upon opening.  */
  4131 void *
  4132 BMenu_new_menu_bar_submenu (void *menu, const char *label)
  4133 {
  4134   BMenu *m = (BMenu *) menu;
  4135   BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
  4136   mn->SetRadioMode (0);
  4137   BMenuItem *i = new BMenuItem (mn);
  4138   i->SetEnabled (1);
  4139   m->AddItem (i);
  4140   return mn;
  4141 }
  4142 
  4143 /* Run MENU, waiting for it to close, and return a pointer to the
  4144    data of the selected item (if one exists), or NULL.  X, Y should
  4145    be in the screen coordinate system.  */
  4146 void *
  4147 BMenu_run (void *menu, int x, int y,
  4148            void (*run_help_callback) (void *, void *),
  4149            void (*block_input_function) (void),
  4150            void (*unblock_input_function) (void),
  4151            struct timespec (*process_pending_signals_function) (void),
  4152            void *run_help_callback_data)
  4153 {
  4154   BPopUpMenu *mn = (BPopUpMenu *) menu;
  4155   enum haiku_event_type type;
  4156   void *buf;
  4157   void *ptr = NULL;
  4158   struct be_popup_menu_data data;
  4159   struct object_wait_info infos[3];
  4160   struct haiku_menu_bar_help_event *event;
  4161   BMessage *msg;
  4162   ssize_t stat;
  4163   struct timespec next_time;
  4164   bigtime_t timeout;
  4165 
  4166   block_input_function ();
  4167   port_popup_menu_to_emacs = create_port (1800, "popup menu port");
  4168   data.x = x;
  4169   data.y = y;
  4170   data.menu = mn;
  4171   unblock_input_function ();
  4172 
  4173   if (port_popup_menu_to_emacs < B_OK)
  4174     return NULL;
  4175 
  4176   block_input_function ();
  4177   mn->SetRadioMode (0);
  4178   buf = alloca (200);
  4179 
  4180   infos[0].object = port_popup_menu_to_emacs;
  4181   infos[0].type = B_OBJECT_TYPE_PORT;
  4182   infos[0].events = B_EVENT_READ;
  4183 
  4184   infos[1].object = spawn_thread (be_popup_menu_thread_entry,
  4185                                   "Menu tracker", B_DEFAULT_MEDIA_PRIORITY,
  4186                                   (void *) &data);
  4187   infos[1].type = B_OBJECT_TYPE_THREAD;
  4188   infos[1].events = B_EVENT_INVALID;
  4189 
  4190   infos[2].object = port_application_to_emacs;
  4191   infos[2].type = B_OBJECT_TYPE_PORT;
  4192   infos[2].events = B_EVENT_READ;
  4193   unblock_input_function ();
  4194 
  4195   if (infos[1].object < B_OK)
  4196     {
  4197       block_input_function ();
  4198       delete_port (port_popup_menu_to_emacs);
  4199       unblock_input_function ();
  4200       return NULL;
  4201     }
  4202 
  4203   block_input_function ();
  4204   resume_thread (infos[1].object);
  4205   unblock_input_function ();
  4206 
  4207   while (true)
  4208     {
  4209       next_time = process_pending_signals_function ();
  4210 
  4211       if (next_time.tv_nsec < 0)
  4212         timeout = 10000000000;
  4213       else
  4214         timeout = (next_time.tv_sec * 1000000
  4215                    + next_time.tv_nsec / 1000);
  4216 
  4217       if ((stat = wait_for_objects_etc ((object_wait_info *) &infos, 3,
  4218                                         B_RELATIVE_TIMEOUT, timeout)) < B_OK)
  4219         {
  4220           if (stat == B_INTERRUPTED || stat == B_TIMED_OUT
  4221               || stat == B_WOULD_BLOCK)
  4222             continue;
  4223           else
  4224             gui_abort ("Failed to wait for popup");
  4225         }
  4226 
  4227       if (infos[0].events & B_EVENT_READ)
  4228         {
  4229           while (!haiku_read_with_timeout (&type, buf, 200, 0, true))
  4230             {
  4231               switch (type)
  4232                 {
  4233                 case MENU_BAR_HELP_EVENT:
  4234                   event = (struct haiku_menu_bar_help_event *) buf;
  4235                   run_help_callback (event->highlight_p
  4236                                      ? event->data
  4237                                      : NULL, run_help_callback_data);
  4238                   break;
  4239                 default:
  4240                   gui_abort ("Unknown popup menu event");
  4241                 }
  4242             }
  4243         }
  4244 
  4245       if (infos[1].events & B_EVENT_INVALID)
  4246         {
  4247           block_input_function ();
  4248           msg = (BMessage *) popup_track_message;
  4249           if (popup_track_message)
  4250             ptr = (void *) msg->GetPointer ("menuptr");
  4251 
  4252           delete_port (port_popup_menu_to_emacs);
  4253           unblock_input_function ();
  4254           return ptr;
  4255         }
  4256 
  4257       infos[0].events = B_EVENT_READ;
  4258       infos[1].events = B_EVENT_INVALID;
  4259       infos[2].events = B_EVENT_READ;
  4260     }
  4261 }
  4262 
  4263 /* Delete the entire menu hierarchy of MENU, and then delete MENU
  4264    itself.  */
  4265 void
  4266 BPopUpMenu_delete (void *menu)
  4267 {
  4268   delete (BPopUpMenu *) menu;
  4269 }
  4270 
  4271 /* Create a menubar, attach it to VIEW, and return it.  */
  4272 void *
  4273 BMenuBar_new (void *view)
  4274 {
  4275   BView *vw = (BView *) view;
  4276   EmacsMenuBar *bar = new EmacsMenuBar ();
  4277 
  4278   if (!vw->LockLooper ())
  4279     gui_abort ("Failed to lock menu bar parent");
  4280   vw->AddChild ((BView *) bar);
  4281   vw->UnlockLooper ();
  4282 
  4283   return bar;
  4284 }
  4285 
  4286 /* Delete MENUBAR along with all subitems. */
  4287 void
  4288 BMenuBar_delete (void *menubar)
  4289 {
  4290   BView *vw = (BView *) menubar;
  4291   BView *p = vw->Parent ();
  4292   EmacsWindow *window = (EmacsWindow *) p->Window ();
  4293 
  4294   if (!p->LockLooper ())
  4295     gui_abort ("Failed to lock menu bar parent while removing menubar");
  4296   window->SetKeyMenuBar (NULL);
  4297   /* MenusEnded isn't called if the menu bar is destroyed
  4298      before it closes.  */
  4299   window->menu_bar_active_p = false;
  4300   vw->RemoveSelf ();
  4301   p->UnlockLooper ();
  4302   delete vw;
  4303 }
  4304 
  4305 /* Delete all items from MENU.  */
  4306 void
  4307 BMenu_delete_all (void *menu)
  4308 {
  4309   BMenu *mn = (BMenu *) menu;
  4310   mn->RemoveItems (0, mn->CountItems (), true);
  4311 }
  4312 
  4313 /* Delete COUNT items from MENU starting from START.  */
  4314 void
  4315 BMenu_delete_from (void *menu, int start, int count)
  4316 {
  4317   BMenu *mn = (BMenu *) menu;
  4318   mn->RemoveItems (start, count, true);
  4319 }
  4320 
  4321 /* Count items in menu MENU.  */
  4322 int
  4323 BMenu_count_items (void *menu)
  4324 {
  4325   return ((BMenu *) menu)->CountItems ();
  4326 }
  4327 
  4328 /* Find the item in MENU at IDX.  */
  4329 void *
  4330 BMenu_item_at (void *menu, int idx)
  4331 {
  4332   return ((BMenu *) menu)->ItemAt (idx);
  4333 }
  4334 
  4335 /* Set ITEM's label to LABEL.  */
  4336 void
  4337 BMenu_item_set_label (void *item, const char *label)
  4338 {
  4339   ((BMenuItem *) item)->SetLabel (label);
  4340 }
  4341 
  4342 /* Get ITEM's menu.  */
  4343 void *
  4344 BMenu_item_get_menu (void *item)
  4345 {
  4346   return ((BMenuItem *) item)->Submenu ();
  4347 }
  4348 
  4349 /* Emit a beep noise.  */
  4350 void
  4351 haiku_ring_bell (void)
  4352 {
  4353   beep ();
  4354 }
  4355 
  4356 /* Create a BAlert with TEXT.  */
  4357 void *
  4358 BAlert_new (const char *text, enum haiku_alert_type type)
  4359 {
  4360   return new BAlert (NULL, text, NULL, NULL, NULL, B_WIDTH_AS_USUAL,
  4361                      (enum alert_type) type);
  4362 }
  4363 
  4364 /* Add a button to ALERT and return the button.  */
  4365 void *
  4366 BAlert_add_button (void *alert, const char *text)
  4367 {
  4368   BAlert *al = (BAlert *) alert;
  4369   al->AddButton (text);
  4370   return al->ButtonAt (al->CountButtons () - 1);
  4371 }
  4372 
  4373 /* Make sure the leftmost button is grouped to the left hand side of
  4374    the alert.  */
  4375 void
  4376 BAlert_set_offset_spacing (void *alert)
  4377 {
  4378   BAlert *al = (BAlert *) alert;
  4379 
  4380   al->SetButtonSpacing (B_OFFSET_SPACING);
  4381 }
  4382 
  4383 static int32
  4384 be_alert_thread_entry (void *thread_data)
  4385 {
  4386   BAlert *alert = (BAlert *) thread_data;
  4387   int32 value;
  4388 
  4389   if (alert->LockLooper ())
  4390     value = alert->Go ();
  4391   else
  4392     value = -1;
  4393 
  4394   alert_popup_value = value;
  4395   return 0;
  4396 }
  4397 
  4398 /* Run ALERT, returning the number of the button that was selected,
  4399    or -1 if no button was selected before the alert was closed.  */
  4400 int32
  4401 BAlert_go (void *alert,
  4402            void (*block_input_function) (void),
  4403            void (*unblock_input_function) (void),
  4404            void (*process_pending_signals_function) (void))
  4405 {
  4406   struct object_wait_info infos[2];
  4407   ssize_t stat;
  4408   BAlert *alert_object = (BAlert *) alert;
  4409 
  4410   infos[0].object = port_application_to_emacs;
  4411   infos[0].type = B_OBJECT_TYPE_PORT;
  4412   infos[0].events = B_EVENT_READ;
  4413 
  4414   block_input_function ();
  4415   /* Alerts are created locked, just like other windows.  */
  4416   alert_object->UnlockLooper ();
  4417   infos[1].object = spawn_thread (be_alert_thread_entry,
  4418                                   "Popup tracker",
  4419                                   B_DEFAULT_MEDIA_PRIORITY,
  4420                                   alert);
  4421   infos[1].type = B_OBJECT_TYPE_THREAD;
  4422   infos[1].events = B_EVENT_INVALID;
  4423   unblock_input_function ();
  4424 
  4425   if (infos[1].object < B_OK)
  4426     return -1;
  4427 
  4428   block_input_function ();
  4429   resume_thread (infos[1].object);
  4430   unblock_input_function ();
  4431 
  4432   while (true)
  4433     {
  4434       stat = wait_for_objects ((object_wait_info *) &infos, 2);
  4435 
  4436       if (stat == B_INTERRUPTED)
  4437         continue;
  4438       else if (stat < B_OK)
  4439         gui_abort ("Failed to wait for popup dialog");
  4440 
  4441       if (infos[1].events & B_EVENT_INVALID)
  4442         return alert_popup_value;
  4443 
  4444       if (infos[0].events & B_EVENT_READ)
  4445         process_pending_signals_function ();
  4446 
  4447       infos[0].events = B_EVENT_READ;
  4448       infos[1].events = B_EVENT_INVALID;
  4449     }
  4450 }
  4451 
  4452 /* Enable or disable BUTTON depending on ENABLED_P.  */
  4453 void
  4454 BButton_set_enabled (void *button, int enabled_p)
  4455 {
  4456   ((BButton *) button)->SetEnabled (enabled_p);
  4457 }
  4458 
  4459 /* Set VIEW's tooltip to TOOLTIP.  */
  4460 void
  4461 BView_set_tooltip (void *view, const char *tooltip)
  4462 {
  4463   ((BView *) view)->SetToolTip (tooltip);
  4464 }
  4465 
  4466 /* Set VIEW's tooltip to a sticky tooltip at X by Y.  */
  4467 void
  4468 be_show_sticky_tooltip (void *view, const char *tooltip_text,
  4469                         int x, int y)
  4470 {
  4471   BToolTip *tooltip;
  4472   BView *vw, *tooltip_view;
  4473   BPoint point;
  4474 
  4475   vw = (BView *) view;
  4476 
  4477   if (!vw->LockLooper ())
  4478     gui_abort ("Failed to lock view while showing sticky tooltip");
  4479 
  4480   vw->SetToolTip ((const char *) NULL);
  4481 
  4482   /* If the tooltip text is empty, then a tooltip object won't be
  4483      created by SetToolTip.  */
  4484   if (tooltip_text[0] == '\0')
  4485     tooltip_text = " ";
  4486 
  4487   vw->SetToolTip (tooltip_text);
  4488 
  4489   tooltip = vw->ToolTip ();
  4490 
  4491   vw->GetMouse (&point, NULL, 1);
  4492   point.x -= x;
  4493   point.y -= y;
  4494 
  4495   point.x = -point.x;
  4496   point.y = -point.y;
  4497 
  4498   /* We don't have to make the tooltip sticky since not receiving
  4499      mouse movement is enough to prevent it from being hidden.  */
  4500   tooltip->SetMouseRelativeLocation (point);
  4501 
  4502   /* Prevent the tooltip from moving in response to mouse
  4503      movement.  */
  4504   tooltip_view = tooltip->View ();
  4505 
  4506   if (tooltip_view)
  4507     tooltip_view->AddChild (new EmacsMotionSuppressionView);
  4508 
  4509   vw->ShowToolTip (tooltip);
  4510   vw->UnlockLooper ();
  4511 }
  4512 
  4513 /* Delete ALERT.  */
  4514 void
  4515 BAlert_delete (void *alert)
  4516 {
  4517   delete (BAlert *) alert;
  4518 }
  4519 
  4520 /* Place the resolution of the monitor in DPI in X_OUT and Y_OUT.  */
  4521 void
  4522 be_get_display_resolution (double *x_out, double *y_out)
  4523 {
  4524   BScreen s (B_MAIN_SCREEN_ID);
  4525   monitor_info i;
  4526   double x_inches, y_inches;
  4527   BRect frame;
  4528 
  4529   if (!s.IsValid ())
  4530     gui_abort ("Invalid screen for resolution checks");
  4531 
  4532   if (s.GetMonitorInfo (&i) == B_OK)
  4533     {
  4534       frame = s.Frame ();
  4535 
  4536       x_inches = (double) i.width * 25.4;
  4537       y_inches = (double) i.height * 25.4;
  4538 
  4539       *x_out = (double) BE_RECT_WIDTH (frame) / x_inches;
  4540       *y_out = (double) BE_RECT_HEIGHT (frame) / y_inches;
  4541       return;
  4542     }
  4543 
  4544   *x_out = 72.0;
  4545   *y_out = 72.0;
  4546 }
  4547 
  4548 /* Add WINDOW to OTHER_WINDOW's subset and parent it to
  4549    OTHER_WINDOW.  */
  4550 void
  4551 EmacsWindow_parent_to (void *window, void *other_window)
  4552 {
  4553   EmacsWindow *w = (EmacsWindow *) window;
  4554   if (!w->LockLooper ())
  4555     gui_abort ("Failed to lock window while parenting");
  4556   w->ParentTo ((EmacsWindow *) other_window);
  4557   w->UnlockLooper ();
  4558 }
  4559 
  4560 void
  4561 EmacsWindow_unparent (void *window)
  4562 {
  4563   EmacsWindow *w = (EmacsWindow *) window;
  4564   if (!w->LockLooper ())
  4565     gui_abort ("Failed to lock window while unparenting");
  4566   w->UnparentAndUnlink ();
  4567   w->UnlockLooper ();
  4568 }
  4569 
  4570 /* Place text describing the current version of Haiku in VERSION,
  4571    which should be a buffer LEN bytes wide.  */
  4572 void
  4573 be_get_version_string (char *version, int len)
  4574 {
  4575   std::strncpy (version, "Unknown Haiku release", len - 1);
  4576   version[len - 1] = '\0';
  4577 
  4578   BPath path;
  4579   if (find_directory (B_BEOS_LIB_DIRECTORY, &path) == B_OK)
  4580     {
  4581       path.Append ("libbe.so");
  4582 
  4583       BAppFileInfo appFileInfo;
  4584       version_info versionInfo;
  4585       BFile file;
  4586       if (file.SetTo (path.Path (), B_READ_ONLY) == B_OK
  4587           && appFileInfo.SetTo (&file) == B_OK
  4588           && appFileInfo.GetVersionInfo (&versionInfo,
  4589                                          B_APP_VERSION_KIND) == B_OK
  4590           && versionInfo.short_info[0] != '\0')
  4591         {
  4592           std::strncpy (version, versionInfo.short_info, len - 1);
  4593           version[len - 1] = '\0';
  4594         }
  4595     }
  4596 }
  4597 
  4598 /* Return the amount of color planes in the current display.  */
  4599 int
  4600 be_get_display_planes (void)
  4601 {
  4602   color_space space = dpy_color_space;
  4603   BScreen screen;
  4604 
  4605   if (space == B_NO_COLOR_SPACE)
  4606     {
  4607       if (!screen.IsValid ())
  4608         gui_abort ("Invalid screen");
  4609 
  4610       space = dpy_color_space = screen.ColorSpace ();
  4611     }
  4612 
  4613   switch (space)
  4614     {
  4615     case B_RGB32:
  4616     case B_RGB24:
  4617       return 24;
  4618     case B_RGB16:
  4619       return 16;
  4620     case B_RGB15:
  4621       return 15;
  4622     case B_CMAP8:
  4623     case B_GRAY8:
  4624       return 8;
  4625     case B_GRAY1:
  4626       return 1;
  4627 
  4628     default:
  4629       gui_abort ("Bad colorspace for screen");
  4630     }
  4631 
  4632   /* https://www.haiku-os.org/docs/api/classBScreen.html
  4633      says a valid screen can't be anything else.  */
  4634   return -1;
  4635 }
  4636 
  4637 /* Return the amount of colors the display can handle.  */
  4638 int
  4639 be_get_display_color_cells (void)
  4640 {
  4641   BScreen screen;
  4642   color_space space = dpy_color_space;
  4643 
  4644   if (space == B_NO_COLOR_SPACE)
  4645     {
  4646       if (!screen.IsValid ())
  4647         gui_abort ("Invalid screen");
  4648 
  4649       space = dpy_color_space = screen.ColorSpace ();
  4650     }
  4651 
  4652   switch (space)
  4653     {
  4654     case B_RGB32:
  4655     case B_RGB24:
  4656       return 16777216;
  4657     case B_RGB16:
  4658       return 65536;
  4659     case B_RGB15:
  4660       return 32768;
  4661     case B_CMAP8:
  4662     case B_GRAY8:
  4663       return 256;
  4664     case B_GRAY1:
  4665       return 2;
  4666 
  4667     default:
  4668       gui_abort ("Bad colorspace for screen");
  4669     }
  4670 
  4671   return -1;
  4672 }
  4673 
  4674 /* Return whether or not the current display is only capable of
  4675    producing grayscale colors.  */
  4676 bool
  4677 be_is_display_grayscale (void)
  4678 {
  4679   BScreen screen;
  4680   color_space space = dpy_color_space;
  4681 
  4682   if (space == B_NO_COLOR_SPACE)
  4683     {
  4684       if (!screen.IsValid ())
  4685         gui_abort ("Invalid screen");
  4686 
  4687       space = dpy_color_space = screen.ColorSpace ();
  4688     }
  4689 
  4690   return space == B_GRAY8 || space == B_GRAY1;
  4691 }
  4692 
  4693 /* Warp the pointer to X by Y.  */
  4694 void
  4695 be_warp_pointer (int x, int y)
  4696 {
  4697   /* We're not supposed to use the following function without a
  4698      BWindowScreen object, but in Haiku nothing actually prevents us
  4699      from doing so.  */
  4700 
  4701   set_mouse_position (x, y);
  4702 }
  4703 
  4704 /* Update the position of CHILD in WINDOW without actually moving
  4705    it.  */
  4706 void
  4707 EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff)
  4708 {
  4709   EmacsWindow *w = (EmacsWindow *) window;
  4710   EmacsWindow *c = (EmacsWindow *) child;
  4711 
  4712   if (!w->LockLooper ())
  4713     gui_abort ("Couldn't lock window for weak move");
  4714   w->MoveChild (c, xoff, yoff, 1);
  4715   w->UnlockLooper ();
  4716 }
  4717 
  4718 /* Find an appropriate view to draw onto.  If VW is double-buffered,
  4719    this will be the view used for double buffering instead of VW
  4720    itself.  */
  4721 void *
  4722 find_appropriate_view_for_draw (void *vw)
  4723 {
  4724   BView *v = (BView *) vw;
  4725   EmacsView *ev = dynamic_cast<EmacsView *>(v);
  4726   if (!ev)
  4727     return v;
  4728 
  4729   return ev->offscreen_draw_view ? ev->offscreen_draw_view : vw;
  4730 }
  4731 
  4732 /* Set up double buffering for VW.  */
  4733 void
  4734 EmacsView_set_up_double_buffering (void *vw)
  4735 {
  4736   EmacsView *view = (EmacsView *) vw;
  4737   if (!view->LockLooper ())
  4738     gui_abort ("Couldn't lock view while setting up double buffering");
  4739   if (view->offscreen_draw_view)
  4740     {
  4741       view->UnlockLooper ();
  4742       return;
  4743     }
  4744   view->SetUpDoubleBuffering ();
  4745   view->UnlockLooper ();
  4746 }
  4747 
  4748 /* Flip and invalidate the view VW.  */
  4749 void
  4750 EmacsView_flip_and_blit (void *vw)
  4751 {
  4752   EmacsView *view = (EmacsView *) vw;
  4753   if (!view->offscreen_draw_view)
  4754     return;
  4755   if (!view->LockLooper ())
  4756     gui_abort ("Couldn't lock view in flip_and_blit");
  4757   view->FlipBuffers ();
  4758   view->UnlockLooper ();
  4759 }
  4760 
  4761 /* Disable double buffering for VW.  */
  4762 void
  4763 EmacsView_disable_double_buffering (void *vw)
  4764 {
  4765   EmacsView *view = (EmacsView *) vw;
  4766   if (!view->LockLooper ())
  4767     gui_abort ("Couldn't lock view tearing down double buffering");
  4768   view->TearDownDoubleBuffering ();
  4769   view->UnlockLooper ();
  4770 }
  4771 
  4772 /* Return non-0 if VW is double-buffered.  */
  4773 int
  4774 EmacsView_double_buffered_p (void *vw)
  4775 {
  4776   EmacsView *view = (EmacsView *) vw;
  4777   if (!view->LockLooper ())
  4778     gui_abort ("Couldn't lock view testing double buffering status");
  4779   int db_p = !!view->offscreen_draw_view;
  4780   view->UnlockLooper ();
  4781   return db_p;
  4782 }
  4783 
  4784 /* Popup a file dialog.  */
  4785 char *
  4786 be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p,
  4787                       int dir_only_p, void *window, const char *save_text,
  4788                       const char *prompt,
  4789                       void (*process_pending_signals_function) (void))
  4790 {
  4791   BWindow *panel_window;
  4792   BEntry path;
  4793   BMessage msg (FILE_PANEL_SELECTION);
  4794   BFilePanel panel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL,
  4795                     NULL, NULL, (dir_only_p
  4796                                  ? B_DIRECTORY_NODE
  4797                                  : B_FILE_NODE | B_DIRECTORY_NODE));
  4798   char *file_name;
  4799   EmacsFilePanelCallbackLooper *looper;
  4800 
  4801   looper = new EmacsFilePanelCallbackLooper;
  4802 
  4803   if (looper->InitCheck () < B_OK)
  4804     {
  4805       delete looper;
  4806       return NULL;
  4807     }
  4808 
  4809   if (default_dir)
  4810     {
  4811       if (path.SetTo (default_dir, 0) != B_OK)
  4812         default_dir = NULL;
  4813     }
  4814 
  4815   panel_window = panel.Window ();
  4816 
  4817   if (default_dir)
  4818     panel.SetPanelDirectory (&path);
  4819 
  4820   if (save_text)
  4821     panel.SetSaveText (save_text);
  4822 
  4823   panel_window->SetTitle (prompt);
  4824   panel_window->SetFeel (B_MODAL_APP_WINDOW_FEEL);
  4825 
  4826   panel.SetHideWhenDone (false);
  4827   panel.SetTarget (BMessenger (looper));
  4828   panel.SetMessage (&msg);
  4829   panel.Show ();
  4830 
  4831   looper->Run ();
  4832   file_name = looper->ReadFileName (process_pending_signals_function);
  4833 
  4834   if (looper->Lock ())
  4835     looper->Quit ();
  4836 
  4837   return file_name;
  4838 }
  4839 
  4840 /* Move the pointer into MBAR and start tracking.  Return whether the
  4841    menu bar was opened correctly.  */
  4842 bool
  4843 BMenuBar_start_tracking (void *mbar)
  4844 {
  4845   EmacsMenuBar *mb = (EmacsMenuBar *) mbar;
  4846   BMessenger messenger (mb);
  4847   BMessage reply;
  4848 
  4849   messenger.SendMessage (SHOW_MENU_BAR, &reply);
  4850 
  4851   return reply.what == BE_MENU_BAR_OPEN;
  4852 }
  4853 
  4854 #ifdef HAVE_NATIVE_IMAGE_API
  4855 int
  4856 be_can_translate_type_to_bitmap_p (const char *mime)
  4857 {
  4858   BTranslatorRoster *r = BTranslatorRoster::Default ();
  4859   translator_id *ids;
  4860   int32 id_len;
  4861 
  4862   if (r->GetAllTranslators (&ids, &id_len) != B_OK)
  4863     return 0;
  4864 
  4865   int found_in = 0;
  4866   int found_out = 0;
  4867 
  4868   for (int i = 0; i < id_len; ++i)
  4869     {
  4870       found_in = 0;
  4871       found_out = 0;
  4872       const translation_format *i_fmts;
  4873       const translation_format *o_fmts;
  4874 
  4875       int32 i_count, o_count;
  4876 
  4877       if (r->GetInputFormats (ids[i], &i_fmts, &i_count) != B_OK)
  4878         continue;
  4879 
  4880       if (r->GetOutputFormats (ids[i], &o_fmts, &o_count) != B_OK)
  4881         continue;
  4882 
  4883       for (int x = 0; x < i_count; ++x)
  4884         {
  4885           if (!strcmp (i_fmts[x].MIME, mime))
  4886             {
  4887               found_in = 1;
  4888               break;
  4889             }
  4890         }
  4891 
  4892       for (int x = 0; x < i_count; ++x)
  4893         {
  4894           if (!strcmp (o_fmts[x].MIME, "image/x-be-bitmap") ||
  4895               !strcmp (o_fmts[x].MIME, "image/x-vnd.Be-bitmap"))
  4896             {
  4897               found_out = 1;
  4898               break;
  4899             }
  4900         }
  4901 
  4902       if (found_in && found_out)
  4903         break;
  4904     }
  4905 
  4906   delete [] ids;
  4907 
  4908   return found_in && found_out;
  4909 }
  4910 
  4911 void *
  4912 be_translate_bitmap_from_file_name (const char *filename)
  4913 {
  4914   BBitmap *bm = BTranslationUtils::GetBitmap (filename);
  4915   return bm;
  4916 }
  4917 
  4918 void *
  4919 be_translate_bitmap_from_memory (const void *buf, size_t bytes)
  4920 {
  4921   BMemoryIO io (buf, bytes);
  4922   BBitmap *bm = BTranslationUtils::GetBitmap (&io);
  4923   return bm;
  4924 }
  4925 #endif
  4926 
  4927 /* Return the size of BITMAP's data, in bytes.  */
  4928 size_t
  4929 BBitmap_bytes_length (void *bitmap)
  4930 {
  4931   BBitmap *bm = (BBitmap *) bitmap;
  4932   return bm->BitsLength ();
  4933 }
  4934 
  4935 /* Show VIEW's tooltip.  */
  4936 void
  4937 BView_show_tooltip (void *view)
  4938 {
  4939   BView *vw = (BView *) view;
  4940   if (vw->LockLooper ())
  4941     {
  4942       vw->ShowToolTip (vw->ToolTip ());
  4943       vw->UnlockLooper ();
  4944     }
  4945 }
  4946 
  4947 
  4948 #ifdef USE_BE_CAIRO
  4949 /* Return VIEW's cairo context.  */
  4950 cairo_t *
  4951 EmacsView_cairo_context (void *view)
  4952 {
  4953   EmacsView *vw = (EmacsView *) view;
  4954   return vw->cr_context;
  4955 }
  4956 
  4957 /* Transfer each clip rectangle in VIEW to the cairo context
  4958    CTX.  */
  4959 void
  4960 BView_cr_dump_clipping (void *view, cairo_t *ctx)
  4961 {
  4962   BView *vw = (BView *) find_appropriate_view_for_draw (view);
  4963   BRegion cr;
  4964   vw->GetClippingRegion (&cr);
  4965 
  4966   for (int i = 0; i < cr.CountRects (); ++i)
  4967     {
  4968       BRect r = cr.RectAt (i);
  4969       cairo_rectangle (ctx, r.left, r.top,
  4970                        BE_RECT_WIDTH (r),
  4971                        BE_RECT_HEIGHT (r));
  4972     }
  4973 
  4974   cairo_clip (ctx);
  4975 }
  4976 
  4977 /* Lock WINDOW in preparation for drawing using Cairo.  */
  4978 void
  4979 EmacsWindow_begin_cr_critical_section (void *window)
  4980 {
  4981   BWindow *w = (BWindow *) window;
  4982   BView *vw = (BView *) w->FindView ("Emacs");
  4983   EmacsView *ev = dynamic_cast <EmacsView *> (vw);
  4984   if (ev && !ev->cr_surface_lock.Lock ())
  4985     gui_abort ("Couldn't lock view cairo surface");
  4986 }
  4987 
  4988 /* Unlock WINDOW in preparation for drawing using Cairo.  */
  4989 void
  4990 EmacsWindow_end_cr_critical_section (void *window)
  4991 {
  4992   BWindow *w = (BWindow *) window;
  4993   BView *vw = (BView *) w->FindView ("Emacs");
  4994   EmacsView *ev = dynamic_cast <EmacsView *> (vw);
  4995   if (ev)
  4996     ev->cr_surface_lock.Unlock ();
  4997 }
  4998 #endif
  4999 
  5000 /* Get the width of STR in the plain font.  */
  5001 int
  5002 be_string_width_with_plain_font (const char *str)
  5003 {
  5004   return be_plain_font->StringWidth (str);
  5005 }
  5006 
  5007 /* Get the ascent + descent of the plain font.  */
  5008 int
  5009 be_plain_font_height (void)
  5010 {
  5011   struct font_height fheight;
  5012   be_plain_font->GetHeight (&fheight);
  5013 
  5014   return fheight.ascent + fheight.descent;
  5015 }
  5016 
  5017 /* Return the number of physical displays connected.  */
  5018 int
  5019 be_get_display_screens (void)
  5020 {
  5021   int count = 1;
  5022   BScreen scr;
  5023 
  5024   if (!scr.IsValid ())
  5025     gui_abort ("Main screen vanished!");
  5026   while (scr.SetToNext () == B_OK && scr.IsValid ())
  5027     ++count;
  5028 
  5029   return count;
  5030 }
  5031 
  5032 /* Set the minimum width the user can resize WINDOW to.  */
  5033 /* Synchronize WINDOW's connection to the App Server.  */
  5034 void
  5035 BWindow_sync (void *window)
  5036 {
  5037   BWindow *w = (BWindow *) window;
  5038 
  5039   if (!w->LockLooper ())
  5040     gui_abort ("Failed to lock window looper for sync");
  5041   w->Sync ();
  5042   w->UnlockLooper ();
  5043 }
  5044 
  5045 /* Set the alignment of WINDOW's dimensions.  */
  5046 void
  5047 BWindow_set_size_alignment (void *window, int align_width, int align_height)
  5048 {
  5049   BWindow *w = (BWindow *) window;
  5050 
  5051   if (!w->LockLooper ())
  5052     gui_abort ("Failed to lock window looper setting alignment");
  5053 #if 0 /* Haiku does not currently implement SetWindowAlignment.  */
  5054   if (w->SetWindowAlignment (B_PIXEL_ALIGNMENT, -1, -1, align_width,
  5055                              align_width, -1, -1, align_height,
  5056                              align_height) != B_NO_ERROR)
  5057     gui_abort ("Invalid pixel alignment");
  5058 #endif
  5059   w->UnlockLooper ();
  5060 }
  5061 
  5062 void
  5063 BWindow_send_behind (void *window, void *other_window)
  5064 {
  5065   BWindow *w = (BWindow *) window;
  5066   BWindow *other = (BWindow *) other_window;
  5067 
  5068   if (!w->LockLooper ())
  5069     gui_abort ("Failed to lock window in order to send it behind another");
  5070   w->SendBehind (other);
  5071   w->UnlockLooper ();
  5072 }
  5073 
  5074 bool
  5075 BWindow_is_active (void *window)
  5076 {
  5077   BWindow *w = (BWindow *) window;
  5078   return w->IsActive ();
  5079 }
  5080 
  5081 bool
  5082 be_use_subpixel_antialiasing (void)
  5083 {
  5084   bool current_subpixel_antialiasing;
  5085 
  5086   if (get_subpixel_antialiasing (&current_subpixel_antialiasing) != B_OK)
  5087     return false;
  5088 
  5089   return current_subpixel_antialiasing;
  5090 }
  5091 
  5092 void
  5093 BWindow_set_override_redirect (void *window, bool override_redirect_p)
  5094 {
  5095   EmacsWindow *w = (EmacsWindow *) window;
  5096 
  5097   if (w->LockLooper ())
  5098     {
  5099       if (override_redirect_p && !w->override_redirect_p)
  5100         {
  5101           w->override_redirect_p = true;
  5102           w->pre_override_redirect_look = w->Look ();
  5103           w->RecomputeFeel ();
  5104           w->SetLook (B_NO_BORDER_WINDOW_LOOK);
  5105           w->pre_override_redirect_workspaces = w->Workspaces ();
  5106           w->SetWorkspaces (B_ALL_WORKSPACES);
  5107         }
  5108       else if (w->override_redirect_p)
  5109         {
  5110           w->override_redirect_p = false;
  5111           w->SetLook (w->pre_override_redirect_look);
  5112           w->RecomputeFeel ();
  5113           w->SetWorkspaces (w->pre_override_redirect_workspaces);
  5114         }
  5115 
  5116       w->UnlockLooper ();
  5117     }
  5118 }
  5119 
  5120 /* Find a resource by the name NAME inside the settings file.  The
  5121    string returned is in UTF-8 encoding, and will stay allocated as
  5122    long as the BApplication (a.k.a display) is alive.  */
  5123 const char *
  5124 be_find_setting (const char *name)
  5125 {
  5126   Emacs *app = (Emacs *) be_app;
  5127   const char *value;
  5128 
  5129   /* Note that this is thread-safe since the constructor of `Emacs'
  5130      runs in the main thread.  */
  5131   if (!app->settings_valid_p)
  5132     return NULL;
  5133 
  5134   if (app->settings.FindString (name, 0, &value) != B_OK)
  5135     return NULL;
  5136 
  5137   return value;
  5138 }
  5139 
  5140 void
  5141 BMessage_delete (void *message)
  5142 {
  5143   delete (BMessage *) message;
  5144 }
  5145 
  5146 static int32
  5147 be_drag_message_thread_entry (void *thread_data)
  5148 {
  5149   BMessenger *messenger;
  5150   BMessage reply;
  5151 
  5152   messenger = (BMessenger *) thread_data;
  5153   messenger->SendMessage (WAIT_FOR_RELEASE, &reply);
  5154 
  5155   return 0;
  5156 }
  5157 
  5158 bool
  5159 be_drag_message (void *view, void *message, bool allow_same_view,
  5160                  void (*block_input_function) (void),
  5161                  void (*unblock_input_function) (void),
  5162                  void (*process_pending_signals_function) (void),
  5163                  bool (*should_quit_function) (void))
  5164 {
  5165   EmacsView *vw = (EmacsView *) view;
  5166   EmacsWindow *window = (EmacsWindow *) vw->Window ();
  5167   BMessage *msg = (BMessage *) message;
  5168   BMessage wait_for_release;
  5169   BMessenger messenger (vw);
  5170   BMessage cancel_message (CANCEL_DROP);
  5171   struct object_wait_info infos[2];
  5172   ssize_t stat;
  5173   thread_id window_thread;
  5174 
  5175   block_input_function ();
  5176 
  5177   if (!allow_same_view)
  5178     window_thread = window->Looper ()->Thread ();
  5179 
  5180   if (!allow_same_view
  5181       && (msg->ReplaceInt64 ("emacs:thread_id", window_thread)
  5182           == B_NAME_NOT_FOUND))
  5183     msg->AddInt64 ("emacs:thread_id", window_thread);
  5184 
  5185   if (!vw->LockLooper ())
  5186     gui_abort ("Failed to lock view looper for drag");
  5187 
  5188   vw->DragMessage (msg, BRect (0, 0, 0, 0));
  5189   vw->UnlockLooper ();
  5190 
  5191   infos[0].object = port_application_to_emacs;
  5192   infos[0].type = B_OBJECT_TYPE_PORT;
  5193   infos[0].events = B_EVENT_READ;
  5194 
  5195   infos[1].object = spawn_thread (be_drag_message_thread_entry,
  5196                                   "Drag waiter thread",
  5197                                   B_DEFAULT_MEDIA_PRIORITY,
  5198                                   (void *) &messenger);
  5199   infos[1].type = B_OBJECT_TYPE_THREAD;
  5200   infos[1].events = B_EVENT_INVALID;
  5201   unblock_input_function ();
  5202 
  5203   if (infos[1].object < B_OK)
  5204     return false;
  5205 
  5206   block_input_function ();
  5207   resume_thread (infos[1].object);
  5208   unblock_input_function ();
  5209 
  5210   drag_and_drop_in_progress = true;
  5211 
  5212   while (true)
  5213     {
  5214       block_input_function ();
  5215       stat = wait_for_objects ((struct object_wait_info *) &infos, 2);
  5216       unblock_input_function ();
  5217 
  5218       if (stat == B_INTERRUPTED || stat == B_TIMED_OUT
  5219           || stat == B_WOULD_BLOCK)
  5220         continue;
  5221 
  5222       if (stat < B_OK)
  5223         gui_abort ("Failed to wait for drag");
  5224 
  5225       if (infos[0].events & B_EVENT_READ)
  5226         process_pending_signals_function ();
  5227 
  5228       if (should_quit_function ())
  5229         {
  5230           /* Do the best we can to prevent something from being
  5231              dropped, since Haiku doesn't provide a way to actually
  5232              cancel drag-and-drop.  */
  5233           if (vw->LockLooper ())
  5234             {
  5235               vw->DragMessage (&cancel_message, BRect (0, 0, 0, 0));
  5236               vw->UnlockLooper ();
  5237             }
  5238 
  5239           messenger.SendMessage (CANCEL_DROP);
  5240           drag_and_drop_in_progress = false;
  5241           return true;
  5242         }
  5243 
  5244       if (infos[1].events & B_EVENT_INVALID)
  5245         {
  5246           drag_and_drop_in_progress = false;
  5247           return false;
  5248         }
  5249 
  5250       infos[0].events = B_EVENT_READ;
  5251       infos[1].events = B_EVENT_INVALID;
  5252     }
  5253 }
  5254 
  5255 bool
  5256 be_drag_and_drop_in_progress (void)
  5257 {
  5258   return drag_and_drop_in_progress;
  5259 }
  5260 
  5261 /* Replay the menu bar click event EVENT.  Return whether or not the
  5262    menu bar actually opened.  */
  5263 bool
  5264 be_replay_menu_bar_event (void *menu_bar,
  5265                           struct haiku_menu_bar_click_event *event)
  5266 {
  5267   BMenuBar *m = (BMenuBar *) menu_bar;
  5268   BMessenger messenger (m);
  5269   BMessage reply, msg (REPLAY_MENU_BAR);
  5270 
  5271   msg.AddPoint ("emacs:point", BPoint (event->x, event->y));
  5272   messenger.SendMessage (&msg, &reply);
  5273   return reply.what == BE_MENU_BAR_OPEN;
  5274 }
  5275 
  5276 void
  5277 BWindow_set_z_group (void *window, enum haiku_z_group z_group)
  5278 {
  5279   EmacsWindow *w = (EmacsWindow *) window;
  5280 
  5281   if (w->LockLooper ())
  5282     {
  5283       if (w->z_group != z_group)
  5284         {
  5285           w->z_group = z_group;
  5286           w->RecomputeFeel ();
  5287 
  5288           if (w->z_group == Z_GROUP_BELOW)
  5289             w->SetFlags (w->Flags () | B_AVOID_FRONT);
  5290           else
  5291             w->SetFlags (w->Flags () & ~B_AVOID_FRONT);
  5292         }
  5293 
  5294       w->UnlockLooper ();
  5295     }
  5296 }
  5297 
  5298 int
  5299 be_get_ui_color (const char *name, uint32_t *color)
  5300 {
  5301   color_which which;
  5302   rgb_color rgb;
  5303 
  5304   which = which_ui_color (name);
  5305 
  5306   if (which == B_NO_COLOR)
  5307     return 1;
  5308 
  5309   rgb = ui_color (which);
  5310   *color = (rgb.blue | rgb.green << 8
  5311             | rgb.red << 16 | 255 << 24);
  5312 
  5313   return 0;
  5314 }
  5315 
  5316 bool
  5317 be_select_font (void (*process_pending_signals_function) (void),
  5318                 bool (*should_quit_function) (void),
  5319                 haiku_font_family_or_style *family,
  5320                 haiku_font_family_or_style *style,
  5321                 int *size, bool allow_monospace_only,
  5322                 int initial_family, int initial_style,
  5323                 int initial_size, bool initial_antialias,
  5324                 bool *disable_antialias)
  5325 {
  5326   EmacsFontSelectionDialog *dialog;
  5327   struct font_selection_dialog_message msg;
  5328   uint32 flags;
  5329   font_family family_buffer;
  5330   font_style style_buffer;
  5331 
  5332   dialog = new EmacsFontSelectionDialog (allow_monospace_only,
  5333                                          initial_family, initial_style,
  5334                                          initial_size, initial_antialias);
  5335   dialog->CenterOnScreen ();
  5336 
  5337   if (dialog->InitCheck () < B_OK)
  5338     {
  5339       dialog->Quit ();
  5340       return false;
  5341     }
  5342 
  5343   dialog->Show ();
  5344   dialog->WaitForChoice (&msg, process_pending_signals_function,
  5345                          should_quit_function);
  5346 
  5347   if (!dialog->LockLooper ())
  5348     gui_abort ("Failed to lock font selection dialog looper");
  5349   dialog->Quit ();
  5350 
  5351   if (msg.cancel)
  5352     return false;
  5353 
  5354   if (get_font_family (msg.family_idx,
  5355                        &family_buffer, &flags) != B_OK
  5356       || get_font_style (family_buffer, msg.style_idx,
  5357                          &style_buffer, &flags) != B_OK)
  5358     return false;
  5359 
  5360   memcpy (family, family_buffer, sizeof *family);
  5361   memcpy (style, style_buffer, sizeof *style);
  5362   *size = msg.size_specified ? msg.size : -1;
  5363   *disable_antialias = msg.disable_antialias;
  5364 
  5365   return true;
  5366 }
  5367 
  5368 void
  5369 BWindow_set_sticky (void *window, bool sticky)
  5370 {
  5371   BWindow *w = (BWindow *) window;
  5372 
  5373   if (w->LockLooper ())
  5374     {
  5375       w->SetFlags (sticky ? (w->Flags ()
  5376                              | B_SAME_POSITION_IN_ALL_WORKSPACES)
  5377                    : w->Flags () & ~B_SAME_POSITION_IN_ALL_WORKSPACES);
  5378 
  5379       w->UnlockLooper ();
  5380     }
  5381 }
  5382 
  5383 status_t
  5384 be_roster_launch (const char *type, const char *file, char **cargs,
  5385                   ptrdiff_t nargs, void *message, team_id *team_id)
  5386 {
  5387   BEntry entry;
  5388   entry_ref ref;
  5389 
  5390   if (type)
  5391     {
  5392       if (message)
  5393         return be_roster->Launch (type, (BMessage *) message,
  5394                                   team_id);
  5395 
  5396       return be_roster->Launch (type, (nargs > INT_MAX
  5397                                        ? INT_MAX : nargs),
  5398                                 cargs, team_id);
  5399     }
  5400 
  5401   if (entry.SetTo (file) != B_OK)
  5402     return B_ERROR;
  5403 
  5404   if (entry.GetRef (&ref) != B_OK)
  5405     return B_ERROR;
  5406 
  5407   if (message)
  5408     return be_roster->Launch (&ref, (BMessage *) message,
  5409                               team_id);
  5410 
  5411   return be_roster->Launch (&ref, (nargs > INT_MAX
  5412                                    ? INT_MAX : nargs),
  5413                             cargs, team_id);
  5414 }
  5415 
  5416 void *
  5417 be_create_pixmap_cursor (void *bitmap, int x, int y)
  5418 {
  5419   BBitmap *bm;
  5420   BCursor *cursor;
  5421 
  5422   bm = (BBitmap *) bitmap;
  5423   cursor = new BCursor (bm, BPoint (x, y));
  5424 
  5425   if (cursor->InitCheck () != B_OK)
  5426     {
  5427       delete cursor;
  5428       return NULL;
  5429     }
  5430 
  5431   return cursor;
  5432 }
  5433 
  5434 void
  5435 be_get_window_decorator_dimensions (void *window, int *left, int *top,
  5436                                     int *right, int *bottom)
  5437 {
  5438   BWindow *wnd;
  5439   BRect frame, window_frame;
  5440 
  5441   wnd = (BWindow *) window;
  5442 
  5443   if (!wnd->LockLooper ())
  5444     gui_abort ("Failed to lock window looper frame");
  5445 
  5446   frame = wnd->DecoratorFrame ();
  5447   window_frame = wnd->Frame ();
  5448 
  5449   if (left)
  5450     *left = window_frame.left - frame.left;
  5451 
  5452   if (top)
  5453     *top = window_frame.top - frame.top;
  5454 
  5455   if (right)
  5456     *right = frame.right - window_frame.right;
  5457 
  5458   if (bottom)
  5459     *bottom = frame.bottom - window_frame.bottom;
  5460 
  5461   wnd->UnlockLooper ();
  5462 }
  5463 
  5464 void
  5465 be_get_window_decorator_frame (void *window, int *left, int *top,
  5466                                int *width, int *height)
  5467 {
  5468   BWindow *wnd;
  5469   BRect frame;
  5470 
  5471   wnd = (BWindow *) window;
  5472 
  5473   if (!wnd->LockLooper ())
  5474     gui_abort ("Failed to lock window looper frame");
  5475 
  5476   frame = wnd->DecoratorFrame ();
  5477 
  5478   *left = frame.left;
  5479   *top = frame.top;
  5480   *width = BE_RECT_WIDTH (frame);
  5481   *height = BE_RECT_HEIGHT (frame);
  5482 
  5483   wnd->UnlockLooper ();
  5484 }
  5485 
  5486 /* Request that a MOVE_EVENT be sent for WINDOW.  This is so that
  5487    frame offsets can be updated after a frame parameter affecting
  5488    decorators changes.  Sending an event instead of updating the
  5489    offsets directly avoids race conditions where events with older
  5490    information are received after the update happens.  */
  5491 void
  5492 be_send_move_frame_event (void *window)
  5493 {
  5494   BWindow *wnd = (BWindow *) window;
  5495   BMessenger msg (wnd);
  5496 
  5497   msg.SendMessage (SEND_MOVE_FRAME_EVENT);
  5498 }
  5499 
  5500 void
  5501 be_lock_window (void *window)
  5502 {
  5503   BWindow *wnd = (BWindow *) window;
  5504 
  5505   if (!wnd->LockLooper ())
  5506     gui_abort ("Failed to lock window looper");
  5507 }
  5508 
  5509 void
  5510 be_unlock_window (void *window)
  5511 {
  5512   BWindow *wnd = (BWindow *) window;
  5513 
  5514   wnd->UnlockLooper ();
  5515 }
  5516 
  5517 void
  5518 be_set_window_fullscreen_mode (void *window, enum haiku_fullscreen_mode mode)
  5519 {
  5520   EmacsWindow *w = (EmacsWindow *) window;
  5521 
  5522   if (!w->LockLooper ())
  5523     gui_abort ("Failed to lock window to set fullscreen mode");
  5524 
  5525   w->SetFullscreen (mode);
  5526   w->UnlockLooper ();
  5527 }
  5528 
  5529 bool
  5530 be_get_explicit_workarea (int *x, int *y, int *width, int *height)
  5531 {
  5532   BDeskbar deskbar;
  5533   BRect zoom;
  5534   deskbar_location location;
  5535 
  5536   location = deskbar.Location ();
  5537 
  5538   if (location != B_DESKBAR_TOP
  5539       && location != B_DESKBAR_BOTTOM)
  5540     return false;
  5541 
  5542   zoom = get_zoom_rect (NULL);
  5543 
  5544   *x = zoom.left;
  5545   *y = zoom.top;
  5546   *width = BE_RECT_WIDTH (zoom);
  5547   *height = BE_RECT_HEIGHT (zoom);
  5548 
  5549   return true;
  5550 }
  5551 
  5552 /* Clear the grab view.  This has to be called manually from some
  5553    places, since we don't get B_MOUSE_UP messages after a popup menu
  5554    is run.  */
  5555 
  5556 void
  5557 be_clear_grab_view (void)
  5558 {
  5559   if (grab_view_locker.Lock ())
  5560     {
  5561       grab_view = NULL;
  5562       grab_view_locker.Unlock ();
  5563     }
  5564 }
  5565 
  5566 void
  5567 be_set_use_frame_synchronization (void *view, bool sync)
  5568 {
  5569   EmacsView *vw;
  5570 
  5571   vw = (EmacsView *) view;
  5572   vw->SetFrameSynchronization (sync);
  5573 }
  5574 
  5575 status_t
  5576 be_write_node_message (const char *path, const char *name, void *message)
  5577 {
  5578   BNode node (path);
  5579   status_t rc;
  5580   ssize_t flat, result;
  5581   char *buffer;
  5582   BMessage *msg;
  5583 
  5584   rc = node.InitCheck ();
  5585   msg = (BMessage *) message;
  5586 
  5587   if (rc < B_OK)
  5588     return rc;
  5589 
  5590   flat = msg->FlattenedSize ();
  5591   if (flat < B_OK)
  5592     return flat;
  5593 
  5594   buffer = new (std::nothrow) char[flat];
  5595   if (!buffer)
  5596     return B_NO_MEMORY;
  5597 
  5598   rc = msg->Flatten (buffer, flat);
  5599   if (rc < B_OK)
  5600     {
  5601       delete[] buffer;
  5602       return rc;
  5603     }
  5604 
  5605   result = node.WriteAttr (name, B_MIME_TYPE, 0,
  5606                            buffer, flat);
  5607   delete[] buffer;
  5608 
  5609   if (result < B_OK)
  5610     return result;
  5611 
  5612   if (result != flat)
  5613     return B_ERROR;
  5614 
  5615   return B_OK;
  5616 }
  5617 
  5618 void
  5619 be_send_message (const char *app_id, void *message)
  5620 {
  5621   BMessenger messenger (app_id);
  5622 
  5623   messenger.SendMessage ((BMessage *) message);
  5624 }

/* [<][>][^][v][top][bottom][index][help] */