1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-04 11:40:22 +00:00

Use a select wrapper around the GLib event loop, thus taking into account GLib

timeouts and event sources.  This simplifies Gtk+-code a lot, and is needed
for handling GConf death/restart.

* xterm.c: #include xgselect.h.
(x_initialize): Call xgselect_initialize.

* xsettings.c (something_changedCB): C++ comments => C comments.
(init_gconf): Do not deal with any GLib file descriptors, xg_select
does that now.

* gtkutil.c (xg_timer, xg_process_timeouts, xg_start_timer)
(xg_stop_timer, menu_grab_callback_cnt, menu_grab_callback)
(scroll_bar_button_cb): Remove.
(create_menus): C++ comments => C comments. Don't bind grab-notify
event.
(xg_create_scroll_bar): Don't bind button-press-event and
button-release-event.

* process.c: Include xgselect.h if defined (USE_GTK) ||
defined (HAVE_GCONF).
(wait_reading_process_output): Call xg_select for the same condition.

* xgselect.c (xg_select): New function to better integrate with
GLib/Gtk event handling.  Needed if GConf daemon dies/restarts.

* xgselect.h: New file, declare xg_select, xgselect_initialize.

* Makefile.in (XOBJ): Add xgselect.o.
This commit is contained in:
Jan Djärv 2009-11-21 15:28:59 +00:00
parent 62a6e103dd
commit 872870b29a
8 changed files with 236 additions and 167 deletions

View File

@ -1,3 +1,31 @@
2009-11-21 Jan Djärv <jan.h.d@swipnet.se>
* xterm.c: #include xgselect.h.
(x_initialize): Call xgselect_initialize.
* xsettings.c (something_changedCB): C++ comments => C comments.
(init_gconf): Do not deal with any GLib file descriptors, xg_select
does that now.
* gtkutil.c (xg_timer, xg_process_timeouts, xg_start_timer)
(xg_stop_timer, menu_grab_callback_cnt, menu_grab_callback)
(scroll_bar_button_cb): Remove.
(create_menus): C++ comments => C comments. Don't bind grab-notify
event.
(xg_create_scroll_bar): Don't bind button-press-event and
button-release-event.
* process.c: Include xgselect.h if defined (USE_GTK) ||
defined (HAVE_GCONF).
(wait_reading_process_output): Call xg_select for the same condition.
* xgselect.c (xg_select): New function to better integrate with
GLib/Gtk event handling. Needed if GConf daemon dies/restarts.
* xgselect.h: New file, declare xg_select, xgselect_initialize.
* Makefile.in (XOBJ): Add xgselect.o.
2009-11-21 Andreas Schwab <schwab@linux-m68k.org>
* character.h (STRING_CHAR, STRING_CHAR_AND_LENGTH): Remove

View File

@ -292,7 +292,7 @@ ALL_OBJC_CFLAGS=$(ALL_CFLAGS) @GNU_OBJC_CFLAGS@
#ifdef HAVE_X_WINDOWS
XMENU_OBJ = xmenu.o
XOBJ= xterm.o xfns.o xselect.o xrdb.o fontset.o xsmfns.o fringe.o image.o \
xsettings.o
xsettings.o xgselect.o
#ifdef HAVE_MENUS

View File

@ -29,7 +29,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "blockinput.h"
#include "syssignal.h"
#include "window.h"
#include "atimer.h"
#include "gtkutil.h"
#include "termhooks.h"
#include "keyboard.h"
@ -181,11 +180,6 @@ xg_display_close (Display *dpy)
/***********************************************************************
Utility functions
***********************************************************************/
/* The timer for scroll bar repetition and menu bar timeouts.
NULL if no timer is started. */
static struct atimer *xg_timer;
/* The next two variables and functions are taken from lwlib. */
static widget_value *widget_value_free_list;
static int malloc_cpt;
@ -426,58 +420,6 @@ xg_set_cursor (w, cursor)
gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
}
/* Timer function called when a timeout occurs for xg_timer.
This function processes all GTK events in a recursive event loop.
This is done because GTK timer events are not seen by Emacs event
detection, Emacs only looks for X events. When a scroll bar has the
pointer (detected by button press/release events below) an Emacs
timer is started, and this function can then check if the GTK timer
has expired by calling the GTK event loop.
Also, when a menu is active, it has a small timeout before it
pops down the sub menu under it. */
static void
xg_process_timeouts (timer)
struct atimer *timer;
{
BLOCK_INPUT;
/* Ideally we would like to just handle timer events, like the Xt version
of this does in xterm.c, but there is no such feature in GTK. */
while (gtk_events_pending ())
gtk_main_iteration ();
UNBLOCK_INPUT;
}
/* Start the xg_timer with an interval of 0.1 seconds, if not already started.
xg_process_timeouts is called when the timer expires. The timer
started is continuous, i.e. runs until xg_stop_timer is called. */
static void
xg_start_timer ()
{
if (! xg_timer)
{
EMACS_TIME interval;
EMACS_SET_SECS_USECS (interval, 0, 100000);
xg_timer = start_atimer (ATIMER_CONTINUOUS,
interval,
xg_process_timeouts,
0);
}
}
/* Stop the xg_timer if started. */
static void
xg_stop_timer ()
{
if (xg_timer)
{
cancel_atimer (xg_timer);
xg_timer = 0;
}
}
/* Insert NODE into linked LIST. */
static void
@ -1895,29 +1837,6 @@ menu_destroy_callback (w, client_data)
unref_cl_data ((xg_menu_cb_data*) client_data);
}
/* Callback called when a menu does a grab or ungrab. That means the
menu has been activated or deactivated.
Used to start a timer so the small timeout the menus in GTK uses before
popping down a menu is seen by Emacs (see xg_process_timeouts above).
W is the widget that does the grab (not used).
UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
CLIENT_DATA is NULL (not used). */
/* Keep track of total number of grabs. */
static int menu_grab_callback_cnt;
static void
menu_grab_callback (GtkWidget *widget,
gboolean ungrab_p,
gpointer client_data)
{
if (ungrab_p) menu_grab_callback_cnt--;
else menu_grab_callback_cnt++;
if (menu_grab_callback_cnt > 0 && ! xg_timer) xg_start_timer ();
else if (menu_grab_callback_cnt == 0 && xg_timer) xg_stop_timer ();
}
/* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
must be non-NULL) and can be inserted into a menu item.
@ -2232,10 +2151,10 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
else
{
wmenu = gtk_menu_bar_new ();
// Set width of menu bar to a small value so it doesn't enlarge
// a small initial frame size. The width will be set to the
// width of the frame later on when it is added to a container.
// height -1: Natural height.
/* Set width of menu bar to a small value so it doesn't enlarge
a small initial frame size. The width will be set to the
width of the frame later on when it is added to a container.
height -1: Natural height. */
gtk_widget_set_size_request (wmenu, 1, -1);
}
@ -2251,9 +2170,6 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
if (deactivate_cb)
g_signal_connect (G_OBJECT (wmenu),
"selection-done", deactivate_cb, 0);
g_signal_connect (G_OBJECT (wmenu),
"grab-notify", G_CALLBACK (menu_grab_callback), 0);
}
if (! menu_bar_p && add_tearoff_p)
@ -3177,34 +3093,6 @@ xg_gtk_scroll_destroy (widget, data)
xg_remove_widget_from_map (id);
}
/* Callback for button press/release events. Used to start timer so that
the scroll bar repetition timer in GTK gets handled.
Also, sets bar->dragging to Qnil when dragging (button release) is done.
WIDGET is the scroll bar widget the event is for (not used).
EVENT contains the event.
USER_DATA points to the struct scrollbar structure.
Returns FALSE to tell GTK that it shall continue propagate the event
to widgets. */
static gboolean
scroll_bar_button_cb (widget, event, user_data)
GtkWidget *widget;
GdkEventButton *event;
gpointer user_data;
{
if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
xg_start_timer ();
else if (event->type == GDK_BUTTON_RELEASE)
{
struct scroll_bar *bar = (struct scroll_bar *) user_data;
if (xg_timer) xg_stop_timer ();
bar->dragging = Qnil;
}
return FALSE;
}
/* Create a scroll bar widget for frame F. Store the scroll bar
in BAR.
SCROLL_CALLBACK is the callback to invoke when the value of the
@ -3246,17 +3134,6 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
G_CALLBACK (xg_gtk_scroll_destroy),
(gpointer) (EMACS_INT) scroll_id);
/* Connect to button press and button release to detect if any scroll bar
has the pointer. */
g_signal_connect (G_OBJECT (wscroll),
"button-press-event",
G_CALLBACK (scroll_bar_button_cb),
(gpointer) bar);
g_signal_connect (G_OBJECT (wscroll),
"button-release-event",
G_CALLBACK (scroll_bar_button_cb),
(gpointer) bar);
/* The scroll bar widget does not draw on a window of its own. Instead
it draws on the parent window, in this case the edit widget. So
whenever the edit widget is cleared, the scroll bar needs to redraw

View File

@ -120,6 +120,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "composite.h"
#include "atimer.h"
#if defined (USE_GTK) || defined (HAVE_GCONF)
#include "xgselect.h"
#endif /* defined (USE_GTK) || defined (HAVE_GCONF) */
Lisp_Object Qprocessp;
Lisp_Object Qrun, Qstop, Qsignal;
Lisp_Object Qopen, Qclosed, Qconnect, Qfailed, Qlisten;
@ -4922,7 +4926,9 @@ wait_reading_process_output (time_limit, microsecs, read_kbd, do_display,
process_output_skip = 0;
}
#endif
#ifdef HAVE_NS
#if defined (USE_GTK) || defined (HAVE_GCONF)
nfds = xg_select
#elif defined (HAVE_NS)
nfds = ns_select
#else
nfds = select

157
src/xgselect.c Normal file
View File

@ -0,0 +1,157 @@
/* Function for handling the GLib event loop.
Copyright (C) 2009
Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "config.h"
#if defined (USE_GTK) || defined (HAVE_GCONF)
#include <glib.h>
#include <errno.h>
#include <setjmp.h>
#include "xgselect.h"
static GPollFD *gfds;
static int gfds_size;
int
xg_select (max_fds, rfds, wfds, efds, timeout)
int max_fds;
SELECT_TYPE *rfds;
SELECT_TYPE *wfds;
SELECT_TYPE *efds;
EMACS_TIME *timeout;
{
SELECT_TYPE all_rfds, all_wfds;
EMACS_TIME tmo, *tmop = timeout;
GMainContext *context = g_main_context_default ();
int have_wfds = wfds != NULL;
int n_gfds = 0, our_tmo = 0, retval = 0, our_fds = 0;
int prio, i, nfds, tmo_in_millisec;
if (rfds) memcpy (&all_rfds, rfds, sizeof (all_rfds));
else FD_ZERO (&all_rfds);
if (wfds) memcpy (&all_wfds, wfds, sizeof (all_rfds));
else FD_ZERO (&all_wfds);
/* Update event sources in GLib. */
g_main_context_pending (context);
do {
if (n_gfds > gfds_size)
{
while (n_gfds > gfds_size)
gfds_size *= 2;
xfree (gfds);
gfds = xmalloc (sizeof (*gfds) * gfds_size);
}
n_gfds = g_main_context_query (context,
G_PRIORITY_LOW,
&tmo_in_millisec,
gfds,
gfds_size);
} while (n_gfds > gfds_size);
for (i = 0; i < n_gfds; ++i)
{
if (gfds[i].events & G_IO_IN)
{
FD_SET (gfds[i].fd, &all_rfds);
if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
}
if (gfds[i].events & G_IO_OUT)
{
FD_SET (gfds[i].fd, &all_wfds);
if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
have_wfds = 1;
}
}
if (tmo_in_millisec >= 0)
{
EMACS_SET_SECS_USECS (tmo, tmo_in_millisec/1000,
1000 * (tmo_in_millisec % 1000));
if (!timeout) our_tmo = 1;
else
{
EMACS_TIME difference;
EMACS_SUB_TIME (difference, tmo, *timeout);
if (EMACS_TIME_NEG_P (difference)) our_tmo = 1;
}
if (our_tmo) tmop = &tmo;
}
nfds = select (max_fds+1, &all_rfds, have_wfds ? &all_wfds : NULL,
efds, tmop);
if (nfds < 0)
retval = nfds;
else if (nfds > 0)
{
for (i = 0; i < max_fds+1; ++i)
{
if (FD_ISSET (i, &all_rfds))
{
if (rfds && FD_ISSET (i, rfds)) ++retval;
else ++our_fds;
}
if (have_wfds && FD_ISSET (i, &all_wfds))
{
if (wfds && FD_ISSET (i, wfds)) ++retval;
else ++our_fds;
}
if (efds && FD_ISSET (i, efds))
++retval;
}
}
if (our_fds > 0 || (nfds == 0 && our_tmo))
{
/* If Gtk+ is in use eventually gtk_main_iteration will be called,
unless retval is zero. */
#ifdef USE_GTK
if (retval == 0)
#endif
while (g_main_context_pending (context))
g_main_context_dispatch (context);
/* To not have to recalculate timeout, return like this. */
if (retval == 0)
{
retval = -1;
errno = EINTR;
}
}
return retval;
}
#endif /* defined (USE_GTK) || defined (HAVE_GCONF) */
void
xgselect_initialize ()
{
#if defined (USE_GTK) || defined (HAVE_GCONF)
gfds_size = 128;
gfds = xmalloc (sizeof (*gfds)*gfds_size);
#endif /* defined (USE_GTK) || defined (HAVE_GCONF) */
}

35
src/xgselect.h Normal file
View File

@ -0,0 +1,35 @@
/* Header for xg_select.
Copyright (C) 2009
Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#ifndef XGSELECT_H
#define XGSELECT_H
#include "lisp.h"
#include "systime.h"
#include "sysselect.h"
extern int xg_select P_ ((int max_fds,
SELECT_TYPE *rfds,
SELECT_TYPE *wfds,
SELECT_TYPE *efds,
EMACS_TIME *timeout));
extern void xgselect_initialize P_ ((void));
#endif /* XGSELECT_H */

View File

@ -82,7 +82,7 @@ something_changedCB (client, cnxn_id, entry, user_data)
const char *value = gconf_value_get_string (v);
int i;
if (current_mono_font != NULL && strcmp (value, current_mono_font) == 0)
return; // No change.
return; /* No change. */
xfree (current_mono_font);
current_mono_font = xstrdup (value);
@ -501,24 +501,6 @@ init_gconf ()
#if defined (HAVE_GCONF) && defined (HAVE_XFT)
int i;
char *s;
/* Should be enough, this is called at startup */
#define N_FDS 1024
int fd_before[N_FDS], fd_before1[N_FDS];
int dummy, n_fds;
GPollFD gfds[N_FDS];
/* To find out which filedecriptors GConf uses, check before and after.
If we do not do this, GConf changes will only happen when Emacs gets
an X event. */
memset (fd_before, 0, sizeof (fd_before));
n_fds = g_main_context_query (g_main_context_default (),
G_PRIORITY_LOW,
&dummy,
gfds,
N_FDS);
for (i = 0; i < n_fds; ++i)
if (gfds[i].fd < N_FDS && gfds[i].fd > 0 && gfds[i].events > 0)
fd_before[gfds[i].fd] = 1;
g_type_init ();
gconf_client = gconf_client_get_default ();
@ -537,25 +519,6 @@ init_gconf ()
SYSTEM_MONO_FONT,
something_changedCB,
NULL, NULL, NULL);
n_fds = g_main_context_query (g_main_context_default (),
G_PRIORITY_LOW,
&dummy,
gfds,
N_FDS);
for (i = 0; i < n_fds; ++i)
if (gfds[i].fd < N_FDS && gfds[i].fd > 0 && gfds[i].events > 0
&& !fd_before[gfds[i].fd])
{
#ifdef F_SETOWN
fcntl (i, F_SETOWN, getpid ());
#endif /* ! defined (F_SETOWN) */
#ifdef SIGIO
if (interrupt_input)
init_sigio (i);
#endif /* ! defined (SIGIO) */
}
#endif /* HAVE_GCONF && HAVE_XFT */
}

View File

@ -87,6 +87,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "font.h"
#include "fontset.h"
#include "xsettings.h"
#include "xgselect.h"
#include "sysselect.h"
#ifdef USE_X_TOOLKIT
@ -10850,6 +10851,8 @@ x_initialize ()
XSetIOErrorHandler (x_io_error_quitter);
signal (SIGPIPE, x_connection_signal);
xgselect_initialize ();
}