1 /* Haiku window system support
2 Copyright (C) 2021 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
18
19 #include <config.h>
20
21 #include "lisp.h"
22 #include "frame.h"
23 #include "keyboard.h"
24 #include "menu.h"
25 #include "buffer.h"
26 #include "blockinput.h"
27
28 #include "haikuterm.h"
29 #include "haiku_support.h"
30
31 static Lisp_Object *volatile menu_item_selection;
32
33 int popup_activated_p = 0;
34
35 struct submenu_stack_cell
36 {
37 void *parent_menu;
38 void *pane;
39 };
40
41 static void
digest_menu_items(void * first_menu,int start,int menu_items_used,int mbar_p)42 digest_menu_items (void *first_menu, int start, int menu_items_used,
43 int mbar_p)
44 {
45 void **menus, **panes;
46 ssize_t menu_len = (menu_items_used + 1 - start) * sizeof *menus;
47 ssize_t pane_len = (menu_items_used + 1 - start) * sizeof *panes;
48
49 menus = alloca (menu_len);
50 panes = alloca (pane_len);
51
52 int i = start, menu_depth = 0;
53
54 memset (menus, 0, menu_len);
55 memset (panes, 0, pane_len);
56
57 void *menu = first_menu;
58
59 menus[0] = first_menu;
60
61 void *window = NULL;
62 void *view = NULL;
63 if (FRAMEP (Vmenu_updating_frame) &&
64 FRAME_LIVE_P (XFRAME (Vmenu_updating_frame)) &&
65 FRAME_HAIKU_P (XFRAME (Vmenu_updating_frame)))
66 {
67 window = FRAME_HAIKU_WINDOW (XFRAME (Vmenu_updating_frame));
68 view = FRAME_HAIKU_VIEW (XFRAME (Vmenu_updating_frame));
69 }
70
71 if (view)
72 BView_draw_lock (view);
73
74 while (i < menu_items_used)
75 {
76 if (NILP (AREF (menu_items, i)))
77 {
78 menus[++menu_depth] = menu;
79 i++;
80 }
81 else if (EQ (AREF (menu_items, i), Qlambda))
82 {
83 panes[menu_depth] = NULL;
84 menu = panes[--menu_depth] ? panes[menu_depth] : menus[menu_depth];
85 i++;
86 }
87 else if (EQ (AREF (menu_items, i), Qquote))
88 i += 1;
89 else if (EQ (AREF (menu_items, i), Qt))
90 {
91 Lisp_Object pane_name, prefix;
92 const char *pane_string;
93
94 if (menu_items_n_panes == 1)
95 {
96 i += MENU_ITEMS_PANE_LENGTH;
97 continue;
98 }
99
100 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
101 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
102
103 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
104 {
105 pane_name = ENCODE_UTF_8 (pane_name);
106 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
107 }
108
109 pane_string = (NILP (pane_name)
110 ? "" : SSDATA (pane_name));
111 if (!NILP (prefix))
112 pane_string++;
113
114 if (strcmp (pane_string, ""))
115 {
116 panes[menu_depth] =
117 menu = BMenu_new_submenu (menus[menu_depth], pane_string, 1);
118 }
119
120 i += MENU_ITEMS_PANE_LENGTH;
121 }
122 else
123 {
124 Lisp_Object item_name, enable, descrip, def, selected, help;
125 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
126 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
127 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
128 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
129 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
130 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
131
132 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
133 {
134 item_name = ENCODE_UTF_8 (item_name);
135 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
136 }
137
138 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
139 {
140 descrip = ENCODE_UTF_8 (descrip);
141 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
142 }
143
144 if (STRINGP (help) && STRING_MULTIBYTE (help))
145 {
146 help = ENCODE_UTF_8 (help);
147 ASET (menu_items, i + MENU_ITEMS_ITEM_HELP, help);
148 }
149
150 if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used &&
151 NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
152 menu = BMenu_new_submenu (menu, SSDATA (item_name), !NILP (enable));
153 else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
154 BMenu_add_separator (menu);
155 else if (!mbar_p)
156 BMenu_add_item (menu, SSDATA (item_name),
157 !NILP (def) ? aref_addr (menu_items, i) : NULL,
158 !NILP (enable), !NILP (selected), 0, window,
159 !NILP (descrip) ? SSDATA (descrip) : NULL,
160 STRINGP (help) ? SSDATA (help) : NULL);
161 else
162 BMenu_add_item (menu, SSDATA (item_name),
163 !NILP (def) ? (void *) (intptr_t) i : NULL,
164 !NILP (enable), !NILP (selected), 1, window,
165 !NILP (descrip) ? SSDATA (descrip) : NULL,
166 STRINGP (help) ? SSDATA (help) : NULL);
167
168 i += MENU_ITEMS_ITEM_LENGTH;
169 }
170 }
171
172 if (view)
173 BView_draw_unlock (view);
174 }
175
176 static Lisp_Object
haiku_dialog_show(struct frame * f,Lisp_Object title,Lisp_Object header,const char ** error_name)177 haiku_dialog_show (struct frame *f, Lisp_Object title,
178 Lisp_Object header, const char **error_name)
179 {
180 int i, nb_buttons = 0;
181
182 *error_name = NULL;
183
184 if (menu_items_n_panes > 1)
185 {
186 *error_name = "Multiple panes in dialog box";
187 return Qnil;
188 }
189
190 Lisp_Object pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
191 i = MENU_ITEMS_PANE_LENGTH;
192
193 if (STRING_MULTIBYTE (pane_name))
194 pane_name = ENCODE_UTF_8 (pane_name);
195
196 block_input ();
197 void *alert = BAlert_new (SSDATA (pane_name), NILP (header) ? HAIKU_INFO_ALERT :
198 HAIKU_IDEA_ALERT);
199
200 Lisp_Object vals[10];
201
202 while (i < menu_items_used)
203 {
204 Lisp_Object item_name, enable, descrip, value;
205 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
206 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
207 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
208 value = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
209
210 if (NILP (item_name))
211 {
212 BAlert_delete (alert);
213 *error_name = "Submenu in dialog items";
214 unblock_input ();
215 return Qnil;
216 }
217
218 if (EQ (item_name, Qquote))
219 {
220 i++;
221 }
222
223 if (nb_buttons >= 9)
224 {
225 BAlert_delete (alert);
226 *error_name = "Too many dialog items";
227 unblock_input ();
228 return Qnil;
229 }
230
231 if (STRING_MULTIBYTE (item_name))
232 item_name = ENCODE_UTF_8 (item_name);
233 if (!NILP (descrip) && STRING_MULTIBYTE (descrip))
234 descrip = ENCODE_UTF_8 (descrip);
235
236 void *button = BAlert_add_button (alert, SSDATA (item_name));
237
238 BButton_set_enabled (button, !NILP (enable));
239 if (!NILP (descrip))
240 BView_set_tooltip (button, SSDATA (descrip));
241
242 vals[nb_buttons] = value;
243 ++nb_buttons;
244 i += MENU_ITEMS_ITEM_LENGTH;
245 }
246
247 int32_t val = BAlert_go (alert);
248 unblock_input ();
249
250 if (val < 0)
251 quit ();
252 else
253 return vals[val];
254
255 return Qnil;
256 }
257
258 Lisp_Object
haiku_popup_dialog(struct frame * f,Lisp_Object header,Lisp_Object contents)259 haiku_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
260 {
261 Lisp_Object title;
262 const char *error_name = NULL;
263 Lisp_Object selection;
264 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
265
266 check_window_system (f);
267
268 /* Decode the dialog items from what was specified. */
269 title = Fcar (contents);
270 CHECK_STRING (title);
271 record_unwind_protect_void (unuse_menu_items);
272
273 if (NILP (Fcar (Fcdr (contents))))
274 /* No buttons specified, add an "Ok" button so users can pop down
275 the dialog. Also, the lesstif/motif version crashes if there are
276 no buttons. */
277 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
278
279 list_of_panes (list1 (contents));
280
281 /* Display them in a dialog box. */
282 block_input ();
283 selection = haiku_dialog_show (f, title, header, &error_name);
284 unblock_input ();
285
286 unbind_to (specpdl_count, Qnil);
287 discard_menu_items ();
288
289 if (error_name)
290 error ("%s", error_name);
291 return selection;
292 }
293
294 Lisp_Object
haiku_menu_show(struct frame * f,int x,int y,int menuflags,Lisp_Object title,const char ** error_name)295 haiku_menu_show (struct frame *f, int x, int y, int menuflags,
296 Lisp_Object title, const char **error_name)
297 {
298 int i = 0, submenu_depth = 0;
299 void *view = FRAME_HAIKU_VIEW (f);
300 void *menu;
301
302 Lisp_Object *subprefix_stack =
303 alloca (menu_items_used * sizeof (Lisp_Object));
304
305 eassert (FRAME_HAIKU_P (f));
306
307 *error_name = NULL;
308
309 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
310 {
311 *error_name = "Empty menu";
312 return Qnil;
313 }
314
315 block_input ();
316 if (STRINGP (title) && STRING_MULTIBYTE (title))
317 title = ENCODE_UTF_8 (title);
318
319 menu = BPopUpMenu_new (STRINGP (title) ? SSDATA (title) : NULL);
320 if (STRINGP (title))
321 {
322 BMenu_add_title (menu, SSDATA (title));
323 BMenu_add_separator (menu);
324 }
325 digest_menu_items (menu, 0, menu_items_used, 0);
326 BView_convert_to_screen (view, &x, &y);
327 unblock_input ();
328
329 menu_item_selection = BMenu_run (menu, x, y);
330
331 FRAME_DISPLAY_INFO (f)->grabbed = 0;
332
333 if (menu_item_selection)
334 {
335 Lisp_Object prefix, entry;
336
337 prefix = entry = Qnil;
338 i = 0;
339 while (i < menu_items_used)
340 {
341 if (NILP (AREF (menu_items, i)))
342 {
343 subprefix_stack[submenu_depth++] = prefix;
344 prefix = entry;
345 i++;
346 }
347 else if (EQ (AREF (menu_items, i), Qlambda))
348 {
349 prefix = subprefix_stack[--submenu_depth];
350 i++;
351 }
352 else if (EQ (AREF (menu_items, i), Qt))
353 {
354 prefix
355 = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
356 i += MENU_ITEMS_PANE_LENGTH;
357 }
358 /* Ignore a nil in the item list.
359 It's meaningful only for dialog boxes. */
360 else if (EQ (AREF (menu_items, i), Qquote))
361 i += 1;
362 else
363 {
364 entry
365 = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
366 if (menu_item_selection == aref_addr (menu_items, i))
367 {
368 if (menuflags & MENU_KEYMAPS)
369 {
370 int j;
371
372 entry = list1 (entry);
373 if (!NILP (prefix))
374 entry = Fcons (prefix, entry);
375 for (j = submenu_depth - 1; j >= 0; j--)
376 if (!NILP (subprefix_stack[j]))
377 entry = Fcons (subprefix_stack[j], entry);
378 }
379 BPopUpMenu_delete (menu);
380 return entry;
381 }
382 i += MENU_ITEMS_ITEM_LENGTH;
383 }
384 }
385 }
386 else if (!(menuflags & MENU_FOR_CLICK))
387 {
388 BPopUpMenu_delete (menu);
389 quit ();
390 }
391 BPopUpMenu_delete (menu);
392 return Qnil;
393 }
394
395 void
free_frame_menubar(struct frame * f)396 free_frame_menubar (struct frame *f)
397 {
398 FRAME_MENU_BAR_LINES (f) = 0;
399 FRAME_MENU_BAR_HEIGHT (f) = 0;
400 FRAME_EXTERNAL_MENU_BAR (f) = 0;
401
402 block_input ();
403 void *mbar = FRAME_HAIKU_MENU_BAR (f);
404 if (mbar)
405 BMenuBar_delete (mbar);
406 if (FRAME_OUTPUT_DATA (f)->menu_bar_open_p)
407 --popup_activated_p;
408 FRAME_OUTPUT_DATA (f)->menu_bar_open_p = 0;
409 unblock_input ();
410
411 adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines);
412 }
413
414 void
initialize_frame_menubar(struct frame * f)415 initialize_frame_menubar (struct frame *f)
416 {
417 /* This function is called before the first chance to redisplay
418 the frame. It has to be, so the frame will have the right size. */
419 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
420 set_frame_menubar (f, true);
421 }
422
423 void
set_frame_menubar(struct frame * f,bool deep_p)424 set_frame_menubar (struct frame *f, bool deep_p)
425 {
426 void *mbar = FRAME_HAIKU_MENU_BAR (f);
427 void *view = FRAME_HAIKU_VIEW (f);
428
429 int first_time_p = 0;
430
431 if (!mbar)
432 {
433 mbar = FRAME_HAIKU_MENU_BAR (f) = BMenuBar_new (view);
434 first_time_p = 1;
435 }
436
437 Lisp_Object items;
438 struct buffer *prev = current_buffer;
439 Lisp_Object buffer;
440 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
441 int previous_menu_items_used = f->menu_bar_items_used;
442 Lisp_Object *previous_items
443 = alloca (previous_menu_items_used * sizeof *previous_items);
444
445 XSETFRAME (Vmenu_updating_frame, f);
446
447 if (!deep_p)
448 {
449 FRAME_OUTPUT_DATA (f)->menu_up_to_date_p = 0;
450 items = FRAME_MENU_BAR_ITEMS (f);
451 Lisp_Object string;
452
453 block_input ();
454 int count = BMenu_count_items (mbar);
455
456 int i;
457 for (i = 0; i < ASIZE (items); i += 4)
458 {
459 string = AREF (items, i + 1);
460
461 if (!STRINGP (string))
462 break;
463
464 if (STRING_MULTIBYTE (string))
465 string = ENCODE_UTF_8 (string);
466
467 if (i / 4 < count)
468 {
469 void *it = BMenu_item_at (mbar, i / 4);
470 BMenu_item_set_label (it, SSDATA (string));
471 }
472 else
473 BMenu_new_menu_bar_submenu (mbar, SSDATA (string));
474 }
475
476 if (i / 4 < count)
477 BMenu_delete_from (mbar, i / 4, count - i / 4 + 1);
478 unblock_input ();
479
480 f->menu_bar_items_used = 0;
481 }
482 else
483 {
484 /* If we are making a new widget, its contents are empty,
485 do always reinitialize them. */
486 if (first_time_p)
487 previous_menu_items_used = 0;
488 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
489 specbind (Qinhibit_quit, Qt);
490 /* Don't let the debugger step into this code
491 because it is not reentrant. */
492 specbind (Qdebug_on_next_call, Qnil);
493
494 record_unwind_save_match_data ();
495 if (NILP (Voverriding_local_map_menu_flag))
496 {
497 specbind (Qoverriding_terminal_local_map, Qnil);
498 specbind (Qoverriding_local_map, Qnil);
499 }
500
501 set_buffer_internal_1 (XBUFFER (buffer));
502
503 /* Run the Lucid hook. */
504 safe_run_hooks (Qactivate_menubar_hook);
505
506 /* If it has changed current-menubar from previous value,
507 really recompute the menubar from the value. */
508 if (! NILP (Vlucid_menu_bar_dirty_flag))
509 call0 (Qrecompute_lucid_menubar);
510 safe_run_hooks (Qmenu_bar_update_hook);
511 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
512
513 items = FRAME_MENU_BAR_ITEMS (f);
514
515 /* Save the frame's previous menu bar contents data. */
516 if (previous_menu_items_used)
517 memcpy (previous_items, xvector_contents (f->menu_bar_vector),
518 previous_menu_items_used * word_size);
519
520 /* Fill in menu_items with the current menu bar contents.
521 This can evaluate Lisp code. */
522 save_menu_items ();
523 menu_items = f->menu_bar_vector;
524 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
525 init_menu_items ();
526 int i;
527 int count = BMenu_count_items (mbar);
528 int subitems = ASIZE (items) / 4;
529
530 int *submenu_start, *submenu_end, *submenu_n_panes;
531 Lisp_Object *submenu_names;
532
533 submenu_start = alloca ((subitems + 1) * sizeof *submenu_start);
534 submenu_end = alloca (subitems * sizeof *submenu_end);
535 submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes);
536 submenu_names = alloca (subitems * sizeof (Lisp_Object));
537
538 for (i = 0; i < subitems; ++i)
539 {
540 Lisp_Object key, string, maps;
541
542 key = AREF (items, i * 4);
543 string = AREF (items, i * 4 + 1);
544 maps = AREF (items, i * 4 + 2);
545
546 if (NILP (string))
547 break;
548
549 if (STRINGP (string) && STRING_MULTIBYTE (string))
550 string = ENCODE_UTF_8 (string);
551
552 submenu_start[i] = menu_items_used;
553 menu_items_n_panes = 0;
554 parse_single_submenu (key, string, maps);
555 submenu_n_panes[i] = menu_items_n_panes;
556 submenu_end[i] = menu_items_used;
557 submenu_names[i] = string;
558 }
559 finish_menu_items ();
560 submenu_start[i] = -1;
561
562 block_input ();
563 for (i = 0; submenu_start[i] >= 0; ++i)
564 {
565 void *mn = NULL;
566 if (i < count)
567 mn = BMenu_item_get_menu (BMenu_item_at (mbar, i));
568 if (mn)
569 BMenu_delete_all (mn);
570 else
571 mn = BMenu_new_menu_bar_submenu (mbar, SSDATA (submenu_names[i]));
572
573 menu_items_n_panes = submenu_n_panes[i];
574 digest_menu_items (mn, submenu_start[i], submenu_end[i], 1);
575 }
576 unblock_input ();
577
578 set_buffer_internal_1 (prev);
579
580 FRAME_OUTPUT_DATA (f)->menu_up_to_date_p = 1;
581 fset_menu_bar_vector (f, menu_items);
582 f->menu_bar_items_used = menu_items_used;
583 }
584 unbind_to (specpdl_count, Qnil);
585 }
586
587 void
run_menu_bar_help_event(struct frame * f,int mb_idx)588 run_menu_bar_help_event (struct frame *f, int mb_idx)
589 {
590 Lisp_Object frame;
591 Lisp_Object vec;
592 Lisp_Object help;
593
594 block_input ();
595 if (!FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
596 {
597 unblock_input ();
598 return;
599 }
600
601 XSETFRAME (frame, f);
602
603 if (mb_idx < 0)
604 {
605 kbd_buffer_store_help_event (frame, Qnil);
606 unblock_input ();
607 return;
608 }
609
610 vec = f->menu_bar_vector;
611 if (mb_idx >= ASIZE (vec))
612 emacs_abort ();
613
614 help = AREF (vec, mb_idx + MENU_ITEMS_ITEM_HELP);
615 if (STRINGP (help) || NILP (help))
616 kbd_buffer_store_help_event (frame, help);
617 unblock_input ();
618 }
619
620 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p,
621 0, 0, 0, doc: /* SKIP: real doc in xmenu.c. */)
622 (void)
623 {
624 return popup_activated_p ? Qt : Qnil;
625 }
626
627 DEFUN ("haiku-menu-bar-open", Fhaiku_menu_bar_open, Shaiku_menu_bar_open, 0, 1, "i",
628 doc: /* Show the menu bar in FRAME.
629
630 Move the mouse pointer onto the first element of FRAME's menu bar, and
631 cause it to be opened. If FRAME is nil or not given, use the selected
632 frame. If FRAME has no menu bar, a pop-up is displayed at the position
633 of the last non-menu event instead. */)
634 (Lisp_Object frame)
635 {
636 struct frame *f = decode_window_system_frame (frame);
637
638 if (FRAME_EXTERNAL_MENU_BAR (f))
639 {
640 if (!FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
641 set_frame_menubar (f, 1);
642 }
643 else
644 {
645 return call2 (Qpopup_menu, call0 (Qmouse_menu_bar_map),
646 last_nonmenu_event);
647 }
648
649 block_input ();
650 BMenuBar_start_tracking (FRAME_HAIKU_MENU_BAR (f));
651 unblock_input ();
652
653 return Qnil;
654 }
655
656 void
syms_of_haikumenu(void)657 syms_of_haikumenu (void)
658 {
659 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
660 DEFSYM (Qpopup_menu, "popup-menu");
661 DEFSYM (Qmouse_menu_bar_map, "mouse-menu-bar-map");
662
663 defsubr (&Smenu_or_popup_active_p);
664 defsubr (&Shaiku_menu_bar_open);
665 return;
666 }
667