From 95ccd1ba47771349e23aedf0981861fd5074bd7e Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 29 Jan 2022 05:06:02 +0000 Subject: [PATCH] Implement real menu help-echo text on Haiku * lisp/tooltip.el (tooltip-show-help): Remove Haiku-specific conditional since that's now taken care of by C code. * src/haiku_io.c (haiku_read_size): (haiku_read_with_timeout): (haiku_write_without_signal): Add parameter `popup_p'. All callers changed. (port_popup_menu_to_emacs): New variable. * src/haiku_support.cc (struct be_popup_menu_data): New structure. (be_popup_menu_thread_entry): New function. (class EmacsMenuItem): New field `menu_ptr'. (Highlight): Send help text to the popup port if this item isn't for a menu bar. (BMenu_add_item): Set menu_ptr appropriately. (BMenu_run): Complete rewrite that allows to read help text from the menu bar port. * src/haiku_support.h (struct haiku_menu_bar_help_event): New fields for popup menus. * src/haikumenu.c (digest_menu_items): Only set help tooltip on popup menus when system tooltips are enabled. (haiku_menu_show_help): (haiku_process_pending_signals_for_menu): New functions. (haiku_menu_show): Pass new callbacks. --- lisp/tooltip.el | 7 +- src/haiku_io.c | 24 +++++-- src/haiku_support.cc | 152 +++++++++++++++++++++++++++++++++++++++---- src/haiku_support.h | 17 +++-- src/haikumenu.c | 47 +++++++++++-- src/haikuterm.c | 8 ++- 6 files changed, 217 insertions(+), 38 deletions(-) diff --git a/lisp/tooltip.el b/lisp/tooltip.el index 2aa487d0454..9d523e79679 100644 --- a/lisp/tooltip.el +++ b/lisp/tooltip.el @@ -375,12 +375,7 @@ It is also called if Tooltip mode is on, for text-only displays." (defun tooltip-show-help (msg) "Function installed as `show-help-function'. MSG is either a help string to display, or nil to cancel the display." - (if (and (display-graphic-p) - ;; On Haiku, system tooltips can't be displayed above - ;; menus. - (or (not (and (eq window-system 'haiku) - haiku-use-system-tooltips)) - (not (menu-or-popup-active-p)))) + (if (and (display-graphic-p)) (let ((previous-help tooltip-help-message)) (setq tooltip-help-message msg) (cond ((null msg) diff --git a/src/haiku_io.c b/src/haiku_io.c index cb7750634cf..109aca782ad 100644 --- a/src/haiku_io.c +++ b/src/haiku_io.c @@ -36,6 +36,10 @@ along with GNU Emacs. If not, see . */ Emacs. */ port_id port_application_to_emacs; +/* The port used to send popup menu messages from the application + thread to Emacs. */ +port_id port_popup_menu_to_emacs; + void haiku_io_init (void) { @@ -98,9 +102,11 @@ haiku_len (enum haiku_event_type type) /* Read the size of the next message into len, returning -1 if the query fails or there is no next message. */ void -haiku_read_size (ssize_t *len) +haiku_read_size (ssize_t *len, bool popup_menu_p) { - port_id from = port_application_to_emacs; + port_id from = (popup_menu_p + ? port_popup_menu_to_emacs + : port_application_to_emacs); ssize_t size; size = port_buffer_size_etc (from, B_TIMEOUT, 0); @@ -129,13 +135,16 @@ haiku_read (enum haiku_event_type *type, void *buf, ssize_t len) } /* The same as haiku_read, but time out after TIMEOUT microseconds. + POPUP_MENU_P means to read from the popup menu port instead. Input is blocked when an attempt to read is in progress. */ int haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len, - time_t timeout) + time_t timeout, bool popup_menu_p) { int32 typ; - port_id from = port_application_to_emacs; + port_id from = (popup_menu_p + ? port_popup_menu_to_emacs + : port_application_to_emacs); block_input (); if (read_port_etc (from, &typ, buf, len, @@ -165,9 +174,12 @@ haiku_write (enum haiku_event_type type, void *buf) } int -haiku_write_without_signal (enum haiku_event_type type, void *buf) +haiku_write_without_signal (enum haiku_event_type type, void *buf, + bool popup_menu_p) { - port_id to = port_application_to_emacs; + port_id to = (popup_menu_p + ? port_popup_menu_to_emacs + : port_application_to_emacs); if (write_port (to, (int32_t) type, buf, haiku_len (type)) < B_OK) return -1; diff --git a/src/haiku_support.cc b/src/haiku_support.cc index 41e5b71182f..05bc410eb28 100644 --- a/src/haiku_support.cc +++ b/src/haiku_support.cc @@ -114,6 +114,8 @@ static BLocker child_frame_lock; static BLocker movement_locker; +static BMessage volatile *popup_track_message; + /* This could be a private API, but it's used by (at least) the Qt port, so it's probably here to stay. */ extern status_t get_subpixel_antialiasing (bool *); @@ -137,6 +139,30 @@ gui_abort (const char *msg) emacs_abort (); } +struct be_popup_menu_data +{ + int x, y; + BPopUpMenu *menu; +}; + +static int32 +be_popup_menu_thread_entry (void *thread_data) +{ + struct be_popup_menu_data *data; + BMenuItem *it; + + data = (struct be_popup_menu_data *) thread_data; + + it = data->menu->Go (BPoint (data->x, data->y)); + + if (it) + popup_track_message = it->Message (); + else + popup_track_message = NULL; + + return 0; +} + /* Convert a raw character RAW produced by the keycode KEY into a key symbol and place it in KEYSYM. @@ -656,8 +682,10 @@ public: else if (msg->GetPointer ("menuptr")) { struct haiku_menu_bar_select_event rq; + rq.window = this; rq.ptr = (void *) msg->GetPointer ("menuptr"); + haiku_write (MENU_BAR_SELECT_EVENT, &rq); } else if (msg->what == 'FPSE' @@ -1607,6 +1635,7 @@ class EmacsMenuItem : public BMenuItem { public: int menu_bar_id = -1; + void *menu_ptr = NULL; void *wind_ptr = NULL; char *key = NULL; char *help = NULL; @@ -1675,16 +1704,23 @@ public: if (help) menu->SetToolTip (highlight_p ? help : NULL); - else if (menu_bar_id >= 0) + else { rq.window = wind_ptr; rq.mb_idx = highlight_p ? menu_bar_id : -1; + rq.highlight_p = highlight_p; + rq.data = menu_ptr; r = Frame (); menu->GetMouse (&pt, &buttons); if (!highlight_p || r.Contains (pt)) - haiku_write (MENU_BAR_HELP_EVENT, &rq); + { + if (menu_bar_id > 0) + haiku_write (MENU_BAR_HELP_EVENT, &rq); + else + haiku_write_without_signal (MENU_BAR_HELP_EVENT, &rq, true); + } } BMenuItem::Highlight (highlight_p); @@ -2353,6 +2389,7 @@ BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p, it->menu_bar_id = (intptr_t) ptr; it->wind_ptr = mbw_ptr; } + it->menu_ptr = ptr; if (ptr) msg->AddPointer ("menuptr", ptr); m->AddItem (it); @@ -2397,20 +2434,109 @@ BMenu_new_menu_bar_submenu (void *menu, const char *label) data of the selected item (if one exists), or NULL. X, Y should be in the screen coordinate system. */ void * -BMenu_run (void *menu, int x, int y) +BMenu_run (void *menu, int x, int y, + void (*run_help_callback) (void *, void *), + void (*block_input_function) (void), + void (*unblock_input_function) (void), + void (*process_pending_signals_function) (void), + void *run_help_callback_data) { BPopUpMenu *mn = (BPopUpMenu *) menu; + enum haiku_event_type type; + void *buf; + void *ptr = NULL; + struct be_popup_menu_data data; + struct object_wait_info infos[2]; + struct haiku_menu_bar_help_event *event; + BMessage *msg; + ssize_t stat; + + block_input_function (); + port_popup_menu_to_emacs = create_port (1800, "popup menu port"); + data.x = x; + data.y = y; + data.menu = mn; + unblock_input_function (); + + if (port_popup_menu_to_emacs < B_OK) + return NULL; + + block_input_function (); mn->SetRadioMode (0); - BMenuItem *it = mn->Go (BPoint (x, y)); - if (it) + buf = alloca (200); + + infos[0].object = port_popup_menu_to_emacs; + infos[0].type = B_OBJECT_TYPE_PORT; + infos[0].events = B_EVENT_READ; + + infos[1].object = spawn_thread (be_popup_menu_thread_entry, + "Menu tracker", B_DEFAULT_MEDIA_PRIORITY, + (void *) &data); + infos[1].type = B_OBJECT_TYPE_THREAD; + infos[1].events = B_EVENT_INVALID; + unblock_input_function (); + + if (infos[1].object < B_OK) { - BMessage *mg = it->Message (); - if (mg) - return (void *) mg->GetPointer ("menuptr"); - else - return NULL; + block_input_function (); + delete_port (port_popup_menu_to_emacs); + unblock_input_function (); + return NULL; + } + + block_input_function (); + resume_thread (infos[1].object); + unblock_input_function (); + + while (true) + { + if ((stat = wait_for_objects_etc ((object_wait_info *) &infos, 2, + B_RELATIVE_TIMEOUT, 10000)) < B_OK) + { + if (stat == B_INTERRUPTED) + continue; + else if (stat == B_TIMED_OUT) + { + process_pending_signals_function (); + continue; + } + else + gui_abort ("Failed to wait for popup"); + } + + if (infos[0].events & B_EVENT_READ) + { + if (!haiku_read_with_timeout (&type, buf, 200, 1000000, true)) + { + switch (type) + { + case MENU_BAR_HELP_EVENT: + event = (struct haiku_menu_bar_help_event *) buf; + run_help_callback (event->highlight_p + ? event->data + : NULL, run_help_callback_data); + break; + default: + gui_abort ("Unknown popup menu event"); + } + } + } + + if (infos[1].events & B_EVENT_INVALID) + { + block_input_function (); + msg = (BMessage *) popup_track_message; + if (popup_track_message) + ptr = (void *) msg->GetPointer ("menuptr"); + + delete_port (port_popup_menu_to_emacs); + unblock_input_function (); + return ptr; + } + + infos[0].events = B_EVENT_READ; + infos[1].events = B_EVENT_INVALID; } - return NULL; } /* Delete the entire menu hierarchy of MENU, and then delete MENU @@ -2864,7 +2990,7 @@ be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int enum haiku_event_type type; char *ptr = NULL; - if (!haiku_read_with_timeout (&type, buf, 200, 1000000)) + if (!haiku_read_with_timeout (&type, buf, 200, 1000000, false)) { block_input_function (); if (type != FILE_PANEL_EVENT) @@ -2878,7 +3004,7 @@ be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int ssize_t b_s; block_input_function (); - haiku_read_size (&b_s); + haiku_read_size (&b_s, false); if (!b_s || ptr || panel->Window ()->IsHidden ()) { c_unbind_to_nil_from_cxx (idx); diff --git a/src/haiku_support.h b/src/haiku_support.h index 8d4dddd90fa..4b0456168d0 100644 --- a/src/haiku_support.h +++ b/src/haiku_support.h @@ -200,6 +200,8 @@ struct haiku_menu_bar_help_event { void *window; int mb_idx; + void *data; + bool highlight_p; }; struct haiku_zoom_event @@ -358,25 +360,27 @@ extern "C" #endif extern port_id port_application_to_emacs; + extern port_id port_popup_menu_to_emacs; extern void haiku_io_init (void); extern void haiku_io_init_in_app_thread (void); extern void - haiku_read_size (ssize_t *len); + haiku_read_size (ssize_t *len, bool popup_menu_p); extern int haiku_read (enum haiku_event_type *type, void *buf, ssize_t len); extern int haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len, - time_t timeout); + time_t timeout, bool popup_menu_p); extern int haiku_write (enum haiku_event_type type, void *buf); extern int - haiku_write_without_signal (enum haiku_event_type type, void *buf); + haiku_write_without_signal (enum haiku_event_type type, void *buf, + bool popup_menu_p); extern void rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l); @@ -679,7 +683,12 @@ extern "C" BMenu_item_at (void *menu, int idx); extern void * - BMenu_run (void *menu, int x, int y); + BMenu_run (void *menu, int x, int y, + void (*run_help_callback) (void *, void *), + void (*block_input_function) (void), + void (*unblock_input_function) (void), + void (*process_pending_signals_function) (void), + void *run_help_callback_data); extern void BPopUpMenu_delete (void *menu); diff --git a/src/haikumenu.c b/src/haikumenu.c index 875f1afb6a2..26eb3dbfe13 100644 --- a/src/haikumenu.c +++ b/src/haikumenu.c @@ -150,11 +150,20 @@ digest_menu_items (void *first_menu, int start, int menu_items_used, else if (NILP (def) && menu_separator_name_p (SSDATA (item_name))) BMenu_add_separator (menu); else if (!mbar_p) - BMenu_add_item (menu, SSDATA (item_name), - !NILP (def) ? aref_addr (menu_items, i) : NULL, - !NILP (enable), !NILP (selected), 0, window, - !NILP (descrip) ? SSDATA (descrip) : NULL, - STRINGP (help) ? SSDATA (help) : NULL); + { + if (!use_system_tooltips || NILP (Fsymbol_value (Qtooltip_mode))) + BMenu_add_item (menu, SSDATA (item_name), + !NILP (def) ? aref_addr (menu_items, i) : NULL, + !NILP (enable), !NILP (selected), 0, window, + !NILP (descrip) ? SSDATA (descrip) : NULL, + NULL); + else + BMenu_add_item (menu, SSDATA (item_name), + !NILP (def) ? aref_addr (menu_items, i) : NULL, + !NILP (enable), !NILP (selected), 0, window, + !NILP (descrip) ? SSDATA (descrip) : NULL, + STRINGP (help) ? SSDATA (help) : NULL); + } else if (!use_system_tooltips || NILP (Fsymbol_value (Qtooltip_mode))) BMenu_add_item (menu, SSDATA (item_name), !NILP (def) ? (void *) (intptr_t) i : NULL, @@ -294,6 +303,27 @@ haiku_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents) return selection; } +static void +haiku_menu_show_help (void *help, void *data) +{ + Lisp_Object *id = (Lisp_Object *) help; + + if (help) + show_help_echo (id[MENU_ITEMS_ITEM_HELP], + Qnil, Qnil, Qnil); + else + show_help_echo (Qnil, Qnil, Qnil, Qnil); +} + +static void +haiku_process_pending_signals_for_menu (void) +{ + process_pending_signals (); + + input_pending = false; + detect_input_pending_run_timers (true); +} + Lisp_Object haiku_menu_show (struct frame *f, int x, int y, int menuflags, Lisp_Object title, const char **error_name) @@ -327,9 +357,14 @@ haiku_menu_show (struct frame *f, int x, int y, int menuflags, } digest_menu_items (menu, 0, menu_items_used, 0); BView_convert_to_screen (view, &x, &y); - menu_item_selection = BMenu_run (menu, x, y); unblock_input (); + popup_activated_p++; + menu_item_selection = BMenu_run (menu, x, y, haiku_menu_show_help, + block_input, unblock_input, + haiku_process_pending_signals_for_menu, NULL); + popup_activated_p--; + FRAME_DISPLAY_INFO (f)->grabbed = 0; if (menu_item_selection) diff --git a/src/haikuterm.c b/src/haikuterm.c index b9eb1d2fc5e..6a84e61add4 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -2559,7 +2559,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit) if (!buf) buf = xmalloc (200); - haiku_read_size (&b_size); + haiku_read_size (&b_size, false); while (b_size >= 0) { enum haiku_event_type type; @@ -2831,6 +2831,8 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit) || !NILP (previous_help_echo_string)) do_help = 1; } + + need_flush = FRAME_DIRTY_P (f); break; } case BUTTON_UP: @@ -3260,7 +3262,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit) break; } - haiku_read_size (&b_size); + haiku_read_size (&b_size, false); if (inev.kind != NO_EVENT) { @@ -3285,7 +3287,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit) for (struct unhandled_event *ev = unhandled_events; ev;) { - haiku_write_without_signal (ev->type, &ev->buffer); + haiku_write_without_signal (ev->type, &ev->buffer, false); struct unhandled_event *old = ev; ev = old->next; xfree (old); -- 2.39.5