void free_frame_menubar (struct frame *f) { block_input (); { HMENU old = GetMenu (FRAME_W32_WINDOW (f)); SetMenu (FRAME_W32_WINDOW (f), NULL); f->output_data.w32->menubar_widget = NULL; DestroyMenu (old); } unblock_input (); }
void x_activate_menubar (struct frame *f) { set_frame_menubar (f, false, true); /* Lock out further menubar changes while active. */ f->output_data.w32->menubar_active = 1; /* Signal input thread to return from WM_INITMENU. */ complete_deferred_msg (FRAME_W32_WINDOW (f), WM_INITMENU, 0); }
/* Signal to the main thread that we have file notifications for it to process. */ static void send_notifications (BYTE *info, DWORD info_size, void *desc, volatile int *terminate) { int done = 0; struct frame *f = SELECTED_FRAME (); /* A single buffer is used to communicate all notifications to the main thread. Since both the main thread and several watcher threads could be active at the same time, we use a critical area and an "in-use" flag to synchronize them. A watcher thread can only put its notifications in the buffer if it acquires the critical area and finds the "in-use" flag reset. The main thread resets the flag after it is done processing notifications. FIXME: is there a better way of dealing with this? */ while (!done && !*terminate) { enter_crit (); if (!notification_buffer_in_use) { if (info_size) memcpy (file_notifications, info, min (info_size, sizeof (file_notifications))); notifications_size = min (info_size, sizeof (file_notifications)); notifications_desc = desc; /* If PostMessage fails, the message queue is full. If that happens, the last thing they will worry about is file notifications. So we effectively discard the notification in that case. */ if ((FRAME_TERMCAP_P (f) /* We send the message to the main (a.k.a. "Lisp") thread, where it will wake up MsgWaitForMultipleObjects inside sys_select, causing it to report that there's some keyboard input available. This will in turn cause w32_console_read_socket to be called, which will pick up the file notifications. */ && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0)) || (FRAME_W32_P (f) && PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0)) /* When we are running in batch mode, there's no one to send a message, so we just signal the data is available and hope sys_select will be called soon and will read the data. */ || (FRAME_INITIAL_P (f) && noninteractive)) notification_buffer_in_use = 1; done = 1; } leave_crit (); if (!done) Sleep (5); } }
/* Signal to the main thread that we have file notifications for it to process. */ static void send_notifications (struct notifications_set *ns) { struct frame *f = SELECTED_FRAME (); /* We add the current notification set to the linked list. Use the critical section to make sure only one thread will access the linked list. */ enter_crit (); ns->next = notifications_set_head; ns->prev = notifications_set_head->prev; ns->prev->next = ns; notifications_set_head->prev = ns; leave_crit(); /* If PostMessage fails, the message queue is full. If that happens, the last thing they will worry about is file notifications. So we effectively discard the notification in that case. */ if (FRAME_TERMCAP_P (f)) /* We send the message to the main (a.k.a. "Lisp") thread, where it will wake up MsgWaitForMultipleObjects inside sys_select, causing it to report that there's some keyboard input available. This will in turn cause w32_console_read_socket to be called, which will pick up the file notifications. */ PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0); else if (FRAME_W32_P (f)) PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0); /* When we are running in batch mode, there's no one to send a message, so we just signal the data is available and hope sys_select will be called soon and will read the data. */ #if 0 else if (FRAME_INITIAL_P (f) && noninteractive) ; #endif }
Lisp_Object w32_menu_show (struct frame *f, int x, int y, int menuflags, Lisp_Object title, const char **error) { int i; int menu_item_selection; HMENU menu; POINT pos; widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0; widget_value **submenu_stack = (widget_value **) alloca (menu_items_used * sizeof (widget_value *)); Lisp_Object *subprefix_stack = (Lisp_Object *) alloca (menu_items_used * word_size); int submenu_depth = 0; bool first_pane; *error = NULL; if (menu_items_n_panes == 0) return Qnil; if (menu_items_used <= MENU_ITEMS_PANE_LENGTH) { *error = "Empty menu"; return Qnil; } block_input (); /* Create a tree of widget_value objects representing the panes and their items. */ wv = make_widget_value ("menu", NULL, true, Qnil); wv->button_type = BUTTON_TYPE_NONE; first_wv = wv; first_pane = true; /* Loop over all panes and items, filling in the tree. */ i = 0; while (i < menu_items_used) { if (EQ (AREF (menu_items, i), Qnil)) { submenu_stack[submenu_depth++] = save_wv; save_wv = prev_wv; prev_wv = 0; first_pane = false; i++; } else if (EQ (AREF (menu_items, i), Qlambda)) { prev_wv = save_wv; save_wv = submenu_stack[--submenu_depth]; first_pane = false; i++; } else if (EQ (AREF (menu_items, i), Qt) && submenu_depth != 0) i += MENU_ITEMS_PANE_LENGTH; /* Ignore a nil in the item list. It's meaningful only for dialog boxes. */ else if (EQ (AREF (menu_items, i), Qquote)) i += 1; else if (EQ (AREF (menu_items, i), Qt)) { /* Create a new pane. */ Lisp_Object pane_name, prefix; const char *pane_string; pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); if (STRINGP (pane_name)) { if (unicode_append_menu) pane_name = ENCODE_UTF_8 (pane_name); else if (STRING_MULTIBYTE (pane_name)) pane_name = ENCODE_SYSTEM (pane_name); ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name); } pane_string = (NILP (pane_name) ? "" : SSDATA (pane_name)); /* If there is just one top-level pane, put all its items directly under the top-level menu. */ if (menu_items_n_panes == 1) pane_string = ""; /* If the pane has a meaningful name, make the pane a top-level menu item with its items as a submenu beneath it. */ if (!(menuflags & MENU_KEYMAPS) && strcmp (pane_string, "")) { wv = make_widget_value (pane_string, NULL, true, Qnil); if (save_wv) save_wv->next = wv; else first_wv->contents = wv; if ((menuflags & MENU_KEYMAPS) && !NILP (prefix)) wv->name++; wv->button_type = BUTTON_TYPE_NONE; save_wv = wv; prev_wv = 0; } else if (first_pane) { save_wv = wv; prev_wv = 0; } first_pane = false; i += MENU_ITEMS_PANE_LENGTH; } else { /* Create a new item within current pane. */ Lisp_Object item_name, enable, descrip, def, type, selected, help; item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE); selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); if (STRINGP (item_name)) { if (unicode_append_menu) item_name = ENCODE_UTF_8 (item_name); else if (STRING_MULTIBYTE (item_name)) item_name = ENCODE_SYSTEM (item_name); ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name); } if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) { descrip = ENCODE_SYSTEM (descrip); ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip); } wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable), STRINGP (help) ? help : Qnil); if (prev_wv) prev_wv->next = wv; else save_wv->contents = wv; if (!NILP (descrip)) wv->key = SSDATA (descrip); /* Use the contents index as call_data, since we are restricted to 16-bits. */ wv->call_data = !NILP (def) ? (void *) (UINT_PTR) i : 0; if (NILP (type)) wv->button_type = BUTTON_TYPE_NONE; else if (EQ (type, QCtoggle)) wv->button_type = BUTTON_TYPE_TOGGLE; else if (EQ (type, QCradio)) wv->button_type = BUTTON_TYPE_RADIO; else emacs_abort (); wv->selected = !NILP (selected); prev_wv = wv; i += MENU_ITEMS_ITEM_LENGTH; } } /* Deal with the title, if it is non-nil. */ if (!NILP (title)) { widget_value *wv_title; widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil); /* Maybe replace this separator with a bitmap or owner-draw item so that it looks better. Having two separators looks odd. */ wv_sep->next = first_wv->contents; if (unicode_append_menu) title = ENCODE_UTF_8 (title); else if (STRING_MULTIBYTE (title)) title = ENCODE_SYSTEM (title); wv_title = make_widget_value (SSDATA (title), NULL, true, Qnil); wv_title->title = TRUE; wv_title->button_type = BUTTON_TYPE_NONE; wv_title->next = wv_sep; first_wv->contents = wv_title; } /* No selection has been chosen yet. */ menu_item_selection = 0; /* Actually create the menu. */ current_popup_menu = menu = CreatePopupMenu (); fill_in_menu (menu, first_wv->contents); /* Adjust coordinates to be root-window-relative. */ pos.x = x; pos.y = y; ClientToScreen (FRAME_W32_WINDOW (f), &pos); /* Display the menu. */ menu_item_selection = SendMessage (FRAME_W32_WINDOW (f), WM_EMACS_TRACKPOPUPMENU, (WPARAM)menu, (LPARAM)&pos); /* Clean up extraneous mouse events which might have been generated during the call. */ discard_mouse_events (); FRAME_DISPLAY_INFO (f)->grabbed = 0; /* Free the widget_value objects we used to specify the contents. */ free_menubar_widget_value_tree (first_wv); DestroyMenu (menu); /* Free the owner-drawn and help-echo menu strings. */ w32_free_menu_strings (FRAME_W32_WINDOW (f)); f->output_data.w32->menubar_active = 0; /* Find the selected item, and its pane, to return the proper value. */ if (menu_item_selection != 0) { Lisp_Object prefix, entry; prefix = entry = Qnil; i = 0; while (i < menu_items_used) { if (EQ (AREF (menu_items, i), Qnil)) { subprefix_stack[submenu_depth++] = prefix; prefix = entry; i++; } else if (EQ (AREF (menu_items, i), Qlambda)) { prefix = subprefix_stack[--submenu_depth]; i++; } else if (EQ (AREF (menu_items, i), Qt)) { prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); i += MENU_ITEMS_PANE_LENGTH; } /* Ignore a nil in the item list. It's meaningful only for dialog boxes. */ else if (EQ (AREF (menu_items, i), Qquote)) i += 1; else { entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE); if (menu_item_selection == i) { if (menuflags & MENU_KEYMAPS) { int j; entry = Fcons (entry, Qnil); if (!NILP (prefix)) entry = Fcons (prefix, entry); for (j = submenu_depth - 1; j >= 0; j--) if (!NILP (subprefix_stack[j])) entry = Fcons (subprefix_stack[j], entry); } unblock_input (); return entry; } i += MENU_ITEMS_ITEM_LENGTH; } } } else if (!(menuflags & MENU_FOR_CLICK)) { unblock_input (); /* Make "Cancel" equivalent to C-g. */ Fsignal (Qquit, Qnil); } unblock_input (); return Qnil; }
void set_frame_menubar (struct frame *f, bool first_time, bool deep_p) { HMENU menubar_widget = f->output_data.w32->menubar_widget; Lisp_Object items; widget_value *wv, *first_wv, *prev_wv = 0; int i, last_i; int *submenu_start, *submenu_end; int *submenu_top_level_items, *submenu_n_panes; /* We must not change the menubar when actually in use. */ if (f->output_data.w32->menubar_active) return; XSETFRAME (Vmenu_updating_frame, f); if (! menubar_widget) deep_p = true; if (deep_p) { /* Make a widget-value tree representing the entire menu trees. */ struct buffer *prev = current_buffer; Lisp_Object buffer; ptrdiff_t specpdl_count = SPECPDL_INDEX (); int previous_menu_items_used = f->menu_bar_items_used; Lisp_Object *previous_items = (Lisp_Object *) alloca (previous_menu_items_used * word_size); /* If we are making a new widget, its contents are empty, do always reinitialize them. */ if (! menubar_widget) previous_menu_items_used = 0; buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents; specbind (Qinhibit_quit, Qt); /* Don't let the debugger step into this code because it is not reentrant. */ specbind (Qdebug_on_next_call, Qnil); record_unwind_save_match_data (); if (NILP (Voverriding_local_map_menu_flag)) { specbind (Qoverriding_terminal_local_map, Qnil); specbind (Qoverriding_local_map, Qnil); } set_buffer_internal_1 (XBUFFER (buffer)); /* Run the hooks. */ safe_run_hooks (Qactivate_menubar_hook); safe_run_hooks (Qmenu_bar_update_hook); fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f))); items = FRAME_MENU_BAR_ITEMS (f); /* Save the frame's previous menu bar contents data. */ if (previous_menu_items_used) memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents, previous_menu_items_used * word_size); /* Fill in menu_items with the current menu bar contents. This can evaluate Lisp code. */ save_menu_items (); menu_items = f->menu_bar_vector; menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0; submenu_start = (int *) alloca (ASIZE (items) * sizeof (int)); submenu_end = (int *) alloca (ASIZE (items) * sizeof (int)); submenu_n_panes = (int *) alloca (ASIZE (items) * sizeof (int)); submenu_top_level_items = (int *) alloca (ASIZE (items) * sizeof (int)); init_menu_items (); for (i = 0; i < ASIZE (items); i += 4) { Lisp_Object key, string, maps; last_i = i; key = AREF (items, i); string = AREF (items, i + 1); maps = AREF (items, i + 2); if (NILP (string)) break; submenu_start[i] = menu_items_used; menu_items_n_panes = 0; submenu_top_level_items[i] = parse_single_submenu (key, string, maps); submenu_n_panes[i] = menu_items_n_panes; submenu_end[i] = menu_items_used; } finish_menu_items (); /* Convert menu_items into widget_value trees to display the menu. This cannot evaluate Lisp code. */ wv = make_widget_value ("menubar", NULL, true, Qnil); wv->button_type = BUTTON_TYPE_NONE; first_wv = wv; for (i = 0; i < last_i; i += 4) { menu_items_n_panes = submenu_n_panes[i]; wv = digest_single_submenu (submenu_start[i], submenu_end[i], submenu_top_level_items[i]); if (prev_wv) prev_wv->next = wv; else first_wv->contents = wv; /* Don't set wv->name here; GC during the loop might relocate it. */ wv->enabled = true; wv->button_type = BUTTON_TYPE_NONE; prev_wv = wv; } set_buffer_internal_1 (prev); /* If there has been no change in the Lisp-level contents of the menu bar, skip redisplaying it. Just exit. */ for (i = 0; i < previous_menu_items_used; i++) if (menu_items_used == i || (!EQ (previous_items[i], AREF (menu_items, i)))) break; if (i == menu_items_used && i == previous_menu_items_used && i != 0) { free_menubar_widget_value_tree (first_wv); discard_menu_items (); unbind_to (specpdl_count, Qnil); return; } fset_menu_bar_vector (f, menu_items); f->menu_bar_items_used = menu_items_used; /* This undoes save_menu_items. */ unbind_to (specpdl_count, Qnil); /* Now GC cannot happen during the lifetime of the widget_value, so it's safe to store data from a Lisp_String, as long as local copies are made when the actual menu is created. Windows takes care of this for normal string items, but not for owner-drawn items or additional item-info. */ wv = first_wv->contents; for (i = 0; i < ASIZE (items); i += 4) { Lisp_Object string; string = AREF (items, i + 1); if (NILP (string)) break; wv->name = SSDATA (string); update_submenu_strings (wv->contents); wv = wv->next; } } else { /* Make a widget-value tree containing just the top level menu bar strings. */ wv = make_widget_value ("menubar", NULL, true, Qnil); wv->button_type = BUTTON_TYPE_NONE; first_wv = wv; items = FRAME_MENU_BAR_ITEMS (f); for (i = 0; i < ASIZE (items); i += 4) { Lisp_Object string; string = AREF (items, i + 1); if (NILP (string)) break; wv = make_widget_value (SSDATA (string), NULL, true, Qnil); wv->button_type = BUTTON_TYPE_NONE; /* This prevents lwlib from assuming this menu item is really supposed to be empty. */ /* The EMACS_INT cast avoids a warning. This value just has to be different from small integers. */ wv->call_data = (void *) (EMACS_INT) (-1); if (prev_wv) prev_wv->next = wv; else first_wv->contents = wv; prev_wv = wv; } /* Forget what we thought we knew about what is in the detailed contents of the menu bar menus. Changing the top level always destroys the contents. */ f->menu_bar_items_used = 0; } /* Create or update the menu bar widget. */ block_input (); if (menubar_widget) { /* Empty current menubar, rather than creating a fresh one. */ while (DeleteMenu (menubar_widget, 0, MF_BYPOSITION)) ; } else { menubar_widget = CreateMenu (); } fill_in_menu (menubar_widget, first_wv->contents); free_menubar_widget_value_tree (first_wv); { HMENU old_widget = f->output_data.w32->menubar_widget; f->output_data.w32->menubar_widget = menubar_widget; SetMenu (FRAME_W32_WINDOW (f), f->output_data.w32->menubar_widget); /* Causes flicker when menu bar is updated DrawMenuBar (FRAME_W32_WINDOW (f)); */ /* Force the window size to be recomputed so that the frame's text area remains the same, if menubar has just been created. */ if (old_widget == NULL) { windows_or_buffers_changed = 23; adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines); } } unblock_input (); }
void menubar_selection_callback (struct frame *f, void * client_data) { Lisp_Object prefix, entry; Lisp_Object vector; Lisp_Object *subprefix_stack; int submenu_depth = 0; int i; if (!f) return; entry = Qnil; subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * word_size); vector = f->menu_bar_vector; prefix = Qnil; i = 0; while (i < f->menu_bar_items_used) { if (EQ (AREF (vector, i), Qnil)) { subprefix_stack[submenu_depth++] = prefix; prefix = entry; i++; } else if (EQ (AREF (vector, i), Qlambda)) { prefix = subprefix_stack[--submenu_depth]; i++; } else if (EQ (AREF (vector, i), Qt)) { prefix = AREF (vector, i + MENU_ITEMS_PANE_PREFIX); i += MENU_ITEMS_PANE_LENGTH; } else { entry = AREF (vector, i + MENU_ITEMS_ITEM_VALUE); /* The UINT_PTR cast avoids a warning. There's no problem as long as pointers have enough bits to hold small integers. */ if ((int) (UINT_PTR) client_data == i) { int j; struct input_event buf; Lisp_Object frame; EVENT_INIT (buf); XSETFRAME (frame, f); buf.kind = MENU_BAR_EVENT; buf.frame_or_window = frame; buf.arg = frame; kbd_buffer_store_event (&buf); for (j = 0; j < submenu_depth; j++) if (!NILP (subprefix_stack[j])) { buf.kind = MENU_BAR_EVENT; buf.frame_or_window = frame; buf.arg = subprefix_stack[j]; kbd_buffer_store_event (&buf); } if (!NILP (prefix)) { buf.kind = MENU_BAR_EVENT; buf.frame_or_window = frame; buf.arg = prefix; kbd_buffer_store_event (&buf); } buf.kind = MENU_BAR_EVENT; buf.frame_or_window = frame; buf.arg = entry; /* Free memory used by owner-drawn and help-echo strings. */ w32_free_menu_strings (FRAME_W32_WINDOW (f)); kbd_buffer_store_event (&buf); f->output_data.w32->menubar_active = 0; return; } i += MENU_ITEMS_ITEM_LENGTH; } } /* Free memory used by owner-drawn and help-echo strings. */ w32_free_menu_strings (FRAME_W32_WINDOW (f)); f->output_data.w32->menubar_active = 0; }
static Lisp_Object simple_dialog_show (struct frame *f, Lisp_Object contents, Lisp_Object header) { int answer; UINT type; Lisp_Object lispy_answer = Qnil, temp = XCAR (contents); type = MB_YESNO; /* Since we only handle Yes/No dialogs, and we already checked is_simple_dialog, we don't need to worry about checking contents to see what type of dialog to use. */ /* Use Unicode if possible, so any language can be displayed. */ if (unicode_message_box) { WCHAR *text; const WCHAR *title; USE_SAFE_ALLOCA; if (STRINGP (temp)) { char *utf8_text = SSDATA (ENCODE_UTF_8 (temp)); /* Be pessimistic about the number of characters needed. Remember characters outside the BMP will take more than one utf16 word, so we cannot simply use the character length of temp. */ int utf8_len = strlen (utf8_text); text = SAFE_ALLOCA ((utf8_len + 1) * sizeof (WCHAR)); utf8to16 ((unsigned char *)utf8_text, utf8_len, text); } else { text = (WCHAR *)L""; } if (NILP (header)) { title = L"Question"; type |= MB_ICONQUESTION; } else { title = L"Information"; type |= MB_ICONINFORMATION; } answer = unicode_message_box (FRAME_W32_WINDOW (f), text, title, type); SAFE_FREE (); } else { const char *text, *title; /* Fall back on ANSI message box, but at least use system encoding so questions representable by the system codepage are encoded properly. */ if (STRINGP (temp)) text = SSDATA (ENCODE_SYSTEM (temp)); else text = ""; if (NILP (header)) { title = "Question"; type |= MB_ICONQUESTION; } else { title = "Information"; type |= MB_ICONINFORMATION; } answer = MessageBox (FRAME_W32_WINDOW (f), text, title, type); } if (answer == IDYES) lispy_answer = build_string ("Yes"); else if (answer == IDNO) lispy_answer = build_string ("No"); else Fsignal (Qquit, Qnil); for (temp = XCDR (contents); CONSP (temp); temp = XCDR (temp)) { Lisp_Object item, name, value; item = XCAR (temp); if (CONSP (item)) { name = XCAR (item); value = XCDR (item); } else { name = item; value = Qnil; } if (!NILP (Fstring_equal (name, lispy_answer))) { return value; } } Fsignal (Qquit, Qnil); return Qnil; }