From d4ad7d26656fa03042b4ed471be485e3047267d0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jan=20Dj=C3=A4rv?= Date: Tue, 10 Nov 2009 19:06:40 +0000 Subject: [PATCH] Bug #4574. Common code for file/font dialog. Handle timers with glib-timers. * keyboard.h: Declare timer_check. * keyboard.c (timer_check_2): New function that does what the old timer_check did. (timer_check): Call timer_check_2 until -1 or a non-zero time is returned, i.e. don't return -1 with timers pending. * process.c: Remove extern declaration of timer_check. * xmenu.c (x_menu_wait_for_event): Remove code that did a timeout even if timer_check returned -1. * gtkutil.c (xg_dialog_response_cb): data is now a struct xg_dialog_data (pop_down_dialog): Destroy widget (if any), cancel timer and unref the event loop. (xg_maybe_add_timer, xg_dialog_run): New functions (bug #4574). (xg_get_file_name, xg_get_font_name): Call xg_dialog_run (bug #4574). Destroy the dialog after xg_dialog_run. --- src/ChangeLog | 21 ++++++ src/gtkutil.c | 197 ++++++++++++++++++++++++++++++++++----------------------- src/keyboard.c | 51 ++++++++++++--- src/keyboard.h | 1 + src/process.c | 1 - src/xmenu.c | 17 ++--- 6 files changed, 188 insertions(+), 100 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index bd07b91fddf..08947a8daeb 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,24 @@ +2009-11-10 Jan Djärv + + * keyboard.h: Declare timer_check. + + * keyboard.c (timer_check_2): New function that does what the old + timer_check did. + (timer_check): Call timer_check_2 until -1 or a non-zero time is + returned, i.e. don't return -1 with timers pending. + + * process.c: Remove extern declaration of timer_check. + + * xmenu.c (x_menu_wait_for_event): Remove code that did a timeout + even if timer_check returned -1. + + * gtkutil.c (xg_dialog_response_cb): data is now a struct xg_dialog_data + (pop_down_dialog): Destroy widget (if any), cancel timer and unref + the event loop. + (xg_maybe_add_timer, xg_dialog_run): New functions (bug #4574). + (xg_get_file_name, xg_get_font_name): Call xg_dialog_run (bug #4574). + Destroy the dialog after xg_dialog_run. + 2009-11-10 Stefan Monnier * menu.c (Fx_popup_menu) [HAVE_NS]: Remove unused vars. diff --git a/src/gtkutil.c b/src/gtkutil.c index 543b868448d..ba9d95e0325 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1224,46 +1224,29 @@ create_dialog (wv, select_cb, deactivate_cb) return wdialog; } - - -/*********************************************************************** - File dialog functions - ***********************************************************************/ -/* Return non-zero if the old file selection dialog is being used. - Return zero if not. */ - -int -xg_uses_old_file_dialog () +struct xg_dialog_data { -#ifdef HAVE_GTK_FILE_BOTH - extern int x_gtk_use_old_file_dialog; - return x_gtk_use_old_file_dialog; -#else /* ! HAVE_GTK_FILE_BOTH */ - -#ifdef HAVE_GTK_FILE_SELECTION_NEW - return 1; -#else - return 0; -#endif - -#endif /* ! HAVE_GTK_FILE_BOTH */ -} - + GMainLoop *loop; + int response; + GtkWidget *w; + guint timerid; +}; /* Function that is called when the file or font dialogs pop down. W is the dialog widget, RESPONSE is the response code. - USER_DATA is what we passed in to g_signal_connect (pointer to int). */ + USER_DATA is what we passed in to g_signal_connect. */ static void xg_dialog_response_cb (w, - response, - user_data) + response, + user_data) GtkDialog *w; gint response; gpointer user_data; { - int *ptr = (int *) user_data; - *ptr = response; + struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data; + dd->response = response; + g_main_loop_quit (dd->loop); } @@ -1274,12 +1257,112 @@ pop_down_dialog (arg) Lisp_Object arg; { struct Lisp_Save_Value *p = XSAVE_VALUE (arg); + struct xg_dialog_data *dd = (struct xg_dialog_data *) p->pointer; + BLOCK_INPUT; - gtk_widget_destroy (GTK_WIDGET (p->pointer)); + if (dd->w) gtk_widget_destroy (dd->w); + if (dd->timerid != 0) g_source_remove (dd->timerid); + + g_main_loop_quit (dd->loop); + g_main_loop_unref (dd->loop); + UNBLOCK_INPUT; + return Qnil; } +/* If there are any emacs timers pending, add a timeout to main loop in DATA. + We pass in DATA as gpointer* so we can use this as a callback. */ + +static gboolean +xg_maybe_add_timer (data) + gpointer data; +{ + struct xg_dialog_data *dd = (struct xg_dialog_data *) data; + EMACS_TIME next_time = timer_check (1); + long secs = EMACS_SECS (next_time); + long usecs = EMACS_USECS (next_time); + + dd->timerid = 0; + + if (secs >= 0 && usecs >= 0 && secs < ((guint)-1)/1000) + { + dd->timerid = g_timeout_add (secs * 1000 + usecs/1000, + xg_maybe_add_timer, + dd); + } + return FALSE; +} + + +/* Pops up a modal dialog W and waits for response. + We don't use gtk_dialog_run because we want to process emacs timers. + The dialog W is not destroyed when this function returns. */ + +static int +xg_dialog_run (f, w) + FRAME_PTR f; + GtkWidget *w; + +{ + int count = SPECPDL_INDEX (); + struct xg_dialog_data dd; + + xg_set_screen (w, f); + gtk_window_set_transient_for (GTK_WINDOW (w), + GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); + gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE); + gtk_window_set_modal (GTK_WINDOW (w), TRUE); + + dd.loop = g_main_loop_new (NULL, FALSE); + dd.response = GTK_RESPONSE_CANCEL; + dd.w = w; + dd.timerid = 0; + + g_signal_connect (G_OBJECT (w), + "response", + G_CALLBACK (xg_dialog_response_cb), + &dd); + /* Don't destroy the widget if closed by the window manager close button. */ + g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL); + gtk_widget_show (w); + + record_unwind_protect (pop_down_dialog, make_save_value (&dd, 0)); + + (void) xg_maybe_add_timer (&dd); + g_main_loop_run (dd.loop); + + dd.w = 0; + unbind_to (count, Qnil); + + return dd.response; +} + + +/*********************************************************************** + File dialog functions + ***********************************************************************/ +/* Return non-zero if the old file selection dialog is being used. + Return zero if not. */ + +int +xg_uses_old_file_dialog () +{ +#ifdef HAVE_GTK_FILE_BOTH + extern int x_gtk_use_old_file_dialog; + return x_gtk_use_old_file_dialog; +#else /* ! HAVE_GTK_FILE_BOTH */ + +#ifdef HAVE_GTK_FILE_SELECTION_NEW + return 1; +#else + return 0; +#endif + +#endif /* ! HAVE_GTK_FILE_BOTH */ +} + + typedef char * (*xg_get_file_func) P_ ((GtkWidget *)); #ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW @@ -1537,7 +1620,6 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) int mustmatch_p, only_dir_p; { GtkWidget *w = 0; - int count = SPECPDL_INDEX (); char *fn = 0; int filesel_done = 0; xg_get_file_func func; @@ -1571,29 +1653,9 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) #endif /* HAVE_GTK_FILE_BOTH */ - xg_set_screen (w, f); gtk_widget_set_name (w, "emacs-filedialog"); - gtk_window_set_transient_for (GTK_WINDOW (w), - GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); - gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE); - gtk_window_set_modal (GTK_WINDOW (w), TRUE); - - g_signal_connect (G_OBJECT (w), - "response", - G_CALLBACK (xg_dialog_response_cb), - &filesel_done); - - /* Don't destroy the widget if closed by the window manager close button. */ - g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL); - gtk_widget_show (w); - - record_unwind_protect (pop_down_dialog, make_save_value (w, 0)); - while (! filesel_done) - { - x_menu_wait_for_event (0); - gtk_main_iteration (); - } + filesel_done = xg_dialog_run (f, w); #if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN) sigunblock (sigmask (__SIGRTMIN)); @@ -1602,8 +1664,7 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) if (filesel_done == GTK_RESPONSE_OK) fn = (*func) (w); - unbind_to (count, Qnil); - + gtk_widget_destroy (w); return fn; } @@ -1622,8 +1683,7 @@ xg_get_font_name (f, default_name) FRAME_PTR f; char *default_name; { - GtkWidget *w = 0; - int count = SPECPDL_INDEX (); + GtkWidget *w; char *fontname = NULL; int done = 0; @@ -1637,27 +1697,9 @@ xg_get_font_name (f, default_name) gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (w), default_name); - xg_set_screen (w, f); gtk_widget_set_name (w, "emacs-fontdialog"); - gtk_window_set_transient_for (GTK_WINDOW (w), - GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f))); - gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE); - gtk_window_set_modal (GTK_WINDOW (w), TRUE); - - g_signal_connect (G_OBJECT (w), "response", - G_CALLBACK (xg_dialog_response_cb), &done); - - /* Don't destroy the widget if closed by the window manager close button. */ - g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL); - - gtk_widget_show (w); - record_unwind_protect (pop_down_dialog, make_save_value (w, 0)); - while (!done) - { - x_menu_wait_for_event (0); - gtk_main_iteration (); - } + done = xg_dialog_run (f, w); #if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN) sigunblock (sigmask (__SIGRTMIN)); @@ -1665,10 +1707,9 @@ xg_get_font_name (f, default_name) if (done == GTK_RESPONSE_OK) fontname = gtk_font_selection_dialog_get_font_name - ((GtkFontSelectionDialog *) w); - - unbind_to (count, Qnil); + (GTK_FONT_SELECTION_DIALOG (w)); + gtk_widget_destroy (w); return fontname; } #endif /* HAVE_FREETYPE */ diff --git a/src/keyboard.c b/src/keyboard.c index 4157d1ebfb3..872bd81ed40 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4509,19 +4509,16 @@ extern Lisp_Object Qapply; disregard elements that are not proper timers. Do not make a circular timer list for the time being. - Returns the number of seconds to wait until the next timer fires. If a - timer is triggering now, return zero seconds. - If no timer is active, return -1 seconds. + Returns the time to wait until the next timer fires. If a + timer is triggering now, return zero. + If no timer is active, return -1. If a timer is ripe, we run it, with quitting turned off. + In that case we return 0 to indicate that a new timer_check_2 call + should be done. */ - DO_IT_NOW is now ignored. It used to mean that we should - run the timer directly instead of queueing a timer-event. - Now we always run timers directly. */ - -EMACS_TIME -timer_check (do_it_now) - int do_it_now; +static EMACS_TIME +timer_check_2 () { EMACS_TIME nexttime; EMACS_TIME now, idleness_now; @@ -4685,7 +4682,12 @@ timer_check (do_it_now) /* Since we have handled the event, we don't need to tell the caller to wake up and do it. */ + /* But the caller must still wait for the next timer, so + return 0 to indicate that. */ } + + EMACS_SET_SECS (nexttime, 0); + EMACS_SET_USECS (nexttime, 0); } else /* When we encounter a timer that is still waiting, @@ -4702,6 +4704,35 @@ timer_check (do_it_now) return nexttime; } + +/* Check whether a timer has fired. To prevent larger problems we simply + disregard elements that are not proper timers. Do not make a circular + timer list for the time being. + + Returns the time to wait until the next timer fires. + If no timer is active, return -1. + + As long as any timer is ripe, we run it. + + DO_IT_NOW is now ignored. It used to mean that we should + run the timer directly instead of queueing a timer-event. + Now we always run timers directly. */ + +EMACS_TIME +timer_check (do_it_now) + int do_it_now; +{ + EMACS_TIME nexttime; + + do + { + nexttime = timer_check_2 (); + } + while (EMACS_SECS (nexttime) == 0 && EMACS_USECS (nexttime) == 0); + + return nexttime; +} + DEFUN ("current-idle-time", Fcurrent_idle_time, Scurrent_idle_time, 0, 0, 0, doc: /* Return the current length of Emacs idleness, or nil. The value when Emacs is idle is a list of three integers. The first has diff --git a/src/keyboard.h b/src/keyboard.h index 5b32595fa63..b23cc13c64d 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -488,6 +488,7 @@ extern void add_user_signal P_ ((int, const char *)); extern int tty_read_avail_input P_ ((struct terminal *, int, struct input_event *)); +extern EMACS_TIME timer_check P_ ((int)); /* arch-tag: 769cbade-1ba9-4950-b886-db265b061aa3 (do not change this comment) */ diff --git a/src/process.c b/src/process.c index 08495d2d8bd..fd94731a413 100644 --- a/src/process.c +++ b/src/process.c @@ -296,7 +296,6 @@ static void create_pty P_ ((Lisp_Object)); static Lisp_Object get_process (); static void exec_sentinel (); -extern EMACS_TIME timer_check (); extern int timers_run; /* Mask of bits indicating the descriptors that we wait for input on. */ diff --git a/src/xmenu.c b/src/xmenu.c index 15e609199b5..172be93a58c 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -405,8 +405,6 @@ x_menu_set_in_use (in_use) void x_menu_wait_for_event (void *data) { - extern EMACS_TIME timer_check P_ ((int)); - /* Another way to do this is to register a timer callback, that can be done in GTK and Xt. But we have to do it like this when using only X anyway, and with callbacks we would have three variants for timer handling @@ -422,7 +420,7 @@ x_menu_wait_for_event (void *data) #endif ) { - EMACS_TIME next_time = timer_check (1); + EMACS_TIME next_time = timer_check (1), *ntp; long secs = EMACS_SECS (next_time); long usecs = EMACS_USECS (next_time); SELECT_TYPE read_fds; @@ -437,15 +435,12 @@ x_menu_wait_for_event (void *data) if (fd > n) n = fd; } - if (secs < 0 || (secs == 0 && usecs == 0)) - { - /* Sometimes timer_check returns -1 (no timers) even if there are - timers. So do a timeout anyway. */ - EMACS_SET_SECS (next_time, 1); - EMACS_SET_USECS (next_time, 0); - } + if (secs < 0 && usecs < 0) + ntp = 0; + else + ntp = &next_time; - select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, &next_time); + select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ntp); } } #endif /* ! MSDOS */ -- 2.11.4.GIT