gtkmain.c 44.2 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Elliot Lee's avatar
Elliot Lee committed
12 13 14
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19
#include <X11/Xlocale.h>	/* so we get the right setlocale */
Elliot Lee's avatar
Elliot Lee committed
20 21
#include <stdio.h>
#include <stdlib.h>
22
#include <string.h>
Elliot Lee's avatar
Elliot Lee committed
23
#include "gtkbutton.h"
24
#include "gtkfeatures.h"
Elliot Lee's avatar
Elliot Lee committed
25 26 27 28 29 30 31 32 33 34 35 36 37
#include "gtkhscrollbar.h"
#include "gtkhseparator.h"
#include "gtkmain.h"
#include "gtkpreview.h"
#include "gtkrc.h"
#include "gtkselection.h"
#include "gtksignal.h"
#include "gtktable.h"
#include "gtktext.h"
#include "gtkvbox.h"
#include "gtkvscrollbar.h"
#include "gtkwidget.h"
#include "gtkwindow.h"
38
#include "gtkprivate.h"
39
#include "gdk/gdki18n.h"
40
#include "../config.h"
41
#include "gtkdebug.h"
Elliot Lee's avatar
Elliot Lee committed
42 43 44 45


/* Private type definitions
 */
46 47 48 49 50 51
typedef struct _GtkInitFunction		 GtkInitFunction;
typedef struct _GtkQuitFunction		 GtkQuitFunction;
typedef struct _GtkTimeoutFunction	 GtkTimeoutFunction;
typedef struct _GtkIdleFunction		 GtkIdleFunction;
typedef struct _GtkInputFunction	 GtkInputFunction;
typedef struct _GtkKeySnooperData	 GtkKeySnooperData;
Elliot Lee's avatar
Elliot Lee committed
52 53 54 55 56 57 58

struct _GtkInitFunction
{
  GtkFunction function;
  gpointer data;
};

59 60
struct _GtkQuitFunction
{
61
  guint id;
62 63 64 65 66 67 68
  guint main_level;
  GtkCallbackMarshal marshal;
  GtkFunction function;
  gpointer data;
  GtkDestroyNotify destroy;
};

Elliot Lee's avatar
Elliot Lee committed
69 70
struct _GtkTimeoutFunction
{
71
  guint tag;
Elliot Lee's avatar
Elliot Lee committed
72 73 74 75
  guint32 start;
  guint32 interval;
  guint32 originterval;
  GtkFunction function;
76
  GtkCallbackMarshal marshal;
Elliot Lee's avatar
Elliot Lee committed
77 78 79 80 81 82
  gpointer data;
  GtkDestroyNotify destroy;
};

struct _GtkIdleFunction
{
83
  guint tag;
84 85
  gint priority;
  GtkCallbackMarshal marshal;
Elliot Lee's avatar
Elliot Lee committed
86 87 88 89 90
  GtkFunction function;
  gpointer data;
  GtkDestroyNotify destroy;
};

91 92 93 94 95 96
struct _GtkInputFunction
{
  GtkCallbackMarshal callback;
  gpointer data;
  GtkDestroyNotify destroy;
};
Elliot Lee's avatar
Elliot Lee committed
97

98 99 100 101
struct _GtkKeySnooperData
{
  GtkKeySnoopFunc func;
  gpointer func_data;
102
  guint id;
103 104
};

105
static void  gtk_exit_func		 (void);
106 107
static gint  gtk_quit_invoke_function	 (GtkQuitFunction    *quitf);
static void  gtk_quit_destroy		 (GtkQuitFunction    *quitf);
108 109 110
static void  gtk_timeout_insert		 (GtkTimeoutFunction *timeoutf);
static void  gtk_handle_current_timeouts (guint32	      the_time);
static void  gtk_handle_current_idles	 (void);
111 112
static gint  gtk_invoke_key_snoopers	 (GtkWidget	     *grab_widget,
					  GdkEvent	     *event);
113 114 115 116 117 118 119 120 121 122
static void  gtk_handle_timeouts	 (void);
static void  gtk_handle_idle		 (void);
static void  gtk_handle_timer		 (void);
static void  gtk_propagate_event	 (GtkWidget	     *widget,
					  GdkEvent	     *event);
static void  gtk_error			 (gchar		     *str);
static void  gtk_warning		 (gchar		     *str);
static void  gtk_message		 (gchar		     *str);
static void  gtk_print			 (gchar		     *str);

123 124 125 126 127 128 129 130
static gint  gtk_idle_remove_from_list    (GList               **list, 
					   guint                 tag, 
					   gpointer              data, 
					   gint                  remove_link);
static gint  gtk_timeout_remove_from_list (GList               **list, 
					   guint                 tag, 
					   gint                  remove_link);

131 132
static gint  gtk_idle_compare		 (gconstpointer      a, 
					  gconstpointer      b);
133

134 135
static gint  gtk_timeout_compare	 (gconstpointer      a, 
					  gconstpointer      b);
136

137 138 139
const guint gtk_major_version = GTK_MAJOR_VERSION;
const guint gtk_minor_version = GTK_MINOR_VERSION;
const guint gtk_micro_version = GTK_MICRO_VERSION;
140 141
const guint gtk_binary_age = GTK_BINARY_AGE;
const guint gtk_interface_age = GTK_INTERFACE_AGE;
142

143
static gboolean iteration_done = FALSE;
144
static guint main_level = 0;
145
static gint gtk_initialized = FALSE;
146 147
static GdkEvent *next_event = NULL;
static GList *current_events = NULL;
Elliot Lee's avatar
Elliot Lee committed
148

149 150
static GSList *grabs = NULL;		   /* A stack of unique grabs. The grabbing
					    *  widget is the first one on the list.
Elliot Lee's avatar
Elliot Lee committed
151
					    */
152
static GList *init_functions = NULL;	   /* A list of init functions.
Elliot Lee's avatar
Elliot Lee committed
153
					    */
154 155
static GList *quit_functions = NULL;	   /* A list of quit functions.
					    */
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182


/* When handling timeouts, the algorithm is to move all of the expired
 * timeouts from timeout_functions to current_timeouts then process
 * them as a batch. If one of the timeouts recursively calls the main
 * loop, then the remainder of the timeouts in current_timeouts will
 * be processed before anything else happens.
 * 
 * Each timeout is procesed as follows:
 *
 * - The timeout is removed from current_timeouts
 * - The timeout is pushed on the running_timeouts stack
 * - The timeout is executed
 * - The timeout stack is popped
 * - If the timeout function wasn't removed from the stack while executing,
 *   and it returned TRUE, it is added back to timeout_functions, otherwise
 *   it is destroyed if necessary.
 *
 * gtk_timeout_remove() works by checking for the timeout in
 * timeout_functions current_timeouts and running_timeouts. If it is
 * found in one of the first two, it is removed and destroyed. If it
 * is found in running_timeouts, it is destroyed and ->data is set to
 * NULL for the stack entry.
 *
 * Idle functions work pretty much identically.  
 */

183
static GList *timeout_functions = NULL;	   /* A list of timeout functions sorted by
Elliot Lee's avatar
Elliot Lee committed
184 185 186 187
					    *  when the length of the time interval
					    *  remaining. Therefore, the first timeout
					    *  function to expire is at the head of
					    *  the list and the last to expire is at
188
					    *  the tail of the list.  */
189
static GList *idle_functions = NULL;	   /* A list of idle functions.
Elliot Lee's avatar
Elliot Lee committed
190 191
					    */

192 193 194
/* The idle functions / timeouts that are currently being processed 
 *  by gtk_handle_current_(timeouts/idles) 
 */
Elliot Lee's avatar
Elliot Lee committed
195 196
static GList *current_idles = NULL;
static GList *current_timeouts = NULL;
197 198 199 200 201 202 203

/* A stack of idle functions / timeouts that are currently being
 * being executed
 */
static GList *running_idles = NULL;
static GList *running_timeouts = NULL;

Elliot Lee's avatar
Elliot Lee committed
204 205
static GMemChunk *timeout_mem_chunk = NULL;
static GMemChunk *idle_mem_chunk = NULL;
206
static GMemChunk *quit_mem_chunk = NULL;
Elliot Lee's avatar
Elliot Lee committed
207

208 209
static GSList *key_snoopers = NULL;

210
static GdkVisual *gtk_visual;		   /* The visual to be used in creating new
Elliot Lee's avatar
Elliot Lee committed
211 212
					    *  widgets.
					    */
213
static GdkColormap *gtk_colormap;	   /* The colormap to be used in creating new
Elliot Lee's avatar
Elliot Lee committed
214 215 216
					    *  widgets.
					    */

217
guint gtk_debug_flags = 0;		   /* Global GTK debug flag */
218 219 220

#ifdef G_ENABLE_DEBUG
static GDebugKey gtk_debug_keys[] = {
221
  {"objects", GTK_DEBUG_OBJECTS},
222 223
  {"misc", GTK_DEBUG_MISC},
  {"signals", GTK_DEBUG_SIGNALS}
224
};
225

226
static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
227

228
#endif /* G_ENABLE_DEBUG */
Elliot Lee's avatar
Elliot Lee committed
229

230
gint gtk_use_mb = -1;
231

Elliot Lee's avatar
Elliot Lee committed
232
void
233
gtk_init (int	 *argc,
Elliot Lee's avatar
Elliot Lee committed
234 235
	  char ***argv)
{
236 237
  gchar *current_locale;

Tim Janik's avatar
Tim Janik committed
238 239 240
  if (gtk_initialized)
    return;

241 242 243 244 245 246
#if	0
  g_set_error_handler (gtk_error);
  g_set_warning_handler (gtk_warning);
  g_set_message_handler (gtk_message);
  g_set_print_handler (gtk_print);
#endif
247
  
248 249
  /* Initialize "gdk". We pass along the 'argc' and 'argv'
   *  parameters as they contain information that GDK uses
Elliot Lee's avatar
Elliot Lee committed
250 251
   */
  gdk_init (argc, argv);
252
  
253
#ifdef G_ENABLE_DEBUG
254 255 256 257 258 259 260 261
  {
    gchar *debug_string = getenv("GTK_DEBUG");
    if (debug_string != NULL)
      gtk_debug_flags = g_parse_debug_string (debug_string,
					      gtk_debug_keys,
					      gtk_ndebug_keys);
  }

262 263
  if (argc && argv)
    {
Owen Taylor's avatar
Owen Taylor committed
264
      gint i, j, k;
265 266 267
      
      for (i = 1; i < *argc;)
	{
268 269
	  if ((strcmp ("--gtk-debug", (*argv)[i]) == 0) ||
	      (strncmp ("--gtk-debug=", (*argv)[i], 12) == 0))
270
	    {
271
	      gchar *equal_pos = strchr ((*argv)[i], '=');
272

273 274 275 276 277 278 279
	      if (equal_pos != NULL)
		{
		  gtk_debug_flags |= g_parse_debug_string (equal_pos+1,
							   gtk_debug_keys,
							   gtk_ndebug_keys);
		}
	      else if ((i + 1) < *argc && (*argv)[i + 1])
280
		{
281 282 283
		  gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
							   gtk_debug_keys,
							   gtk_ndebug_keys);
284
		  (*argv)[i] = NULL;
285 286
		  i += 1;
		}
287
	      (*argv)[i] = NULL;
288
	    }
289 290
	  else if ((strcmp ("--gtk-no-debug", (*argv)[i]) == 0) ||
		   (strncmp ("--gtk-no-debug=", (*argv)[i], 15) == 0))
291
	    {
292
	      gchar *equal_pos = strchr ((*argv)[i], '=');
293

294 295 296 297 298 299 300
	      if (equal_pos != NULL)
		{
		  gtk_debug_flags &= ~g_parse_debug_string (equal_pos+1,
							    gtk_debug_keys,
							    gtk_ndebug_keys);
		}
	      else if ((i + 1) < *argc && (*argv)[i + 1])
301 302 303 304
		{
		  gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
							    gtk_debug_keys,
							    gtk_ndebug_keys);
305
		  (*argv)[i] = NULL;
306 307
		  i += 1;
		}
308
	      (*argv)[i] = NULL;
309
	    }
310 311
	  else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
	    {
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
	      GLogLevelFlags fatal_levels;

	      fatal_levels = g_log_set_fatal_mask (g_log_domain_glib, G_LOG_FATAL_MASK);
	      fatal_levels |= G_LOG_LEVEL_WARNING;
              g_log_set_fatal_mask (g_log_domain_glib, fatal_levels);
	      (*argv)[i] = NULL;
	    }
	  else if (strcmp ("--gdk-fatal-warnings", (*argv)[i]) == 0)
	    {
	      GLogLevelFlags fatal_levels;

	      fatal_levels = g_log_set_fatal_mask ("Gdk", G_LOG_FATAL_MASK);
	      fatal_levels |= G_LOG_LEVEL_WARNING;
              g_log_set_fatal_mask ("Gdk", fatal_levels);
	      (*argv)[i] = NULL;
	    }
	  else if (strcmp ("--gtk-fatal-warnings", (*argv)[i]) == 0)
	    {
	      GLogLevelFlags fatal_levels;

	      fatal_levels = g_log_set_fatal_mask (G_LOG_DOMAIN, G_LOG_FATAL_MASK);
	      fatal_levels |= G_LOG_LEVEL_WARNING;
              g_log_set_fatal_mask (G_LOG_DOMAIN, fatal_levels);
335 336
	      (*argv)[i] = NULL;
	    }
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
	  else if (strcmp ("--g-fatal-checks", (*argv)[i]) == 0)
	    {
	      GLogLevelFlags fatal_levels;

	      fatal_levels = g_log_set_fatal_mask (g_log_domain_glib, G_LOG_FATAL_MASK);
	      fatal_levels |= G_LOG_LEVEL_CRITICAL;
              g_log_set_fatal_mask (g_log_domain_glib, fatal_levels);
	      (*argv)[i] = NULL;
	    }
	  else if (strcmp ("--gdk-fatal-checks", (*argv)[i]) == 0)
	    {
	      GLogLevelFlags fatal_levels;

	      fatal_levels = g_log_set_fatal_mask ("Gdk", G_LOG_FATAL_MASK);
	      fatal_levels |= G_LOG_LEVEL_CRITICAL;
              g_log_set_fatal_mask ("Gdk", fatal_levels);
	      (*argv)[i] = NULL;
	    }
	  else if (strcmp ("--gtk-fatal-checks", (*argv)[i]) == 0)
	    {
	      GLogLevelFlags fatal_levels;

	      fatal_levels = g_log_set_fatal_mask (G_LOG_DOMAIN, G_LOG_FATAL_MASK);
	      fatal_levels |= G_LOG_LEVEL_CRITICAL;
              g_log_set_fatal_mask (G_LOG_DOMAIN, fatal_levels);
	      (*argv)[i] = NULL;
	    }
364 365
	  i += 1;
	}
Owen Taylor's avatar
Owen Taylor committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

      for (i = 1; i < *argc; i++)
	{
	  for (k = i; k < *argc; k++)
	    if ((*argv)[k] != NULL)
	      break;
	  
	  if (k > i)
	    {
	      k -= i;
	      for (j = i + k; j < *argc; j++)
		(*argv)[j-k] = (*argv)[j];
	      *argc -= k;
	    }
	}
381 382 383 384
    }

#endif /* G_ENABLE_DEBUG */

385 386
  /* Check if there is a good chance the mb functions will handle things
   * correctly - set if either mblen("\xc0", MB_CUR_MAX) == 1 in the
387
   * C locale, or we're using X's mb functions. (-DX_LOCALE && locale != C)
388 389
   */

390
  current_locale = g_strdup (setlocale (LC_CTYPE, NULL));
391 392

#ifdef X_LOCALE
Owen Taylor's avatar
Owen Taylor committed
393
  if ((strcmp (current_locale, "C")) && (strcmp (current_locale, "POSIX")))
394 395 396 397
    gtk_use_mb = TRUE;
  else
#endif
    {
Owen Taylor's avatar
Owen Taylor committed
398
      setlocale (LC_CTYPE, "C");
399
      gtk_use_mb = (mblen ("\xc0", MB_CUR_MAX) == 1);
Owen Taylor's avatar
Owen Taylor committed
400
      setlocale (LC_CTYPE, current_locale);
401 402
    }

403
  g_free (current_locale);
404

405
  GTK_NOTE (MISC, g_print("%s multi-byte string functions.\n", 
406 407
			  gtk_use_mb ? "Using" : "Not using"));

Elliot Lee's avatar
Elliot Lee committed
408 409 410 411 412 413
  /* Initialize the default visual and colormap to be
   *  used in creating widgets. (We want to use the system
   *  defaults so as to be nice to the colormap).
   */
  gtk_visual = gdk_visual_get_system ();
  gtk_colormap = gdk_colormap_get_system ();
414 415 416

  gtk_type_init ();
  gtk_signal_init ();
Elliot Lee's avatar
Elliot Lee committed
417
  gtk_rc_init ();
418 419
  
  
Elliot Lee's avatar
Elliot Lee committed
420 421 422 423
  /* Register an exit function to make sure we are able to cleanup.
   */
  if (ATEXIT (gtk_exit_func))
    g_warning ("unable to register exit function");
424
  
Elliot Lee's avatar
Elliot Lee committed
425 426
  /* Set the 'initialized' flag.
   */
427
  gtk_initialized = TRUE;
Elliot Lee's avatar
Elliot Lee committed
428 429 430 431 432 433 434
}

void
gtk_exit (int errorcode)
{
  /* Only if "gtk" has been initialized should we de-initialize.
   */
435 436 437
  /* de-initialisation is done by the gtk_exit_funct(),
   * no need to do this here (Alex J.)
   */
Elliot Lee's avatar
Elliot Lee committed
438 439 440 441
  gdk_exit(errorcode);
}

gchar*
442
gtk_set_locale (void)
Elliot Lee's avatar
Elliot Lee committed
443 444 445 446 447
{
  return gdk_set_locale ();
}

void
448
gtk_main (void)
Elliot Lee's avatar
Elliot Lee committed
449 450 451 452 453
{
  GList *tmp_list;
  GList *functions;
  GtkInitFunction *init;
  int old_done;
454 455 456
  
  main_level++;
  
Elliot Lee's avatar
Elliot Lee committed
457 458
  tmp_list = functions = init_functions;
  init_functions = NULL;
459
  
Elliot Lee's avatar
Elliot Lee committed
460 461 462 463
  while (tmp_list)
    {
      init = tmp_list->data;
      tmp_list = tmp_list->next;
464
      
Elliot Lee's avatar
Elliot Lee committed
465 466 467 468
      (* init->function) (init->data);
      g_free (init);
    }
  g_list_free (functions);
469
  
470
  old_done = iteration_done;
Elliot Lee's avatar
Elliot Lee committed
471 472
  while (!gtk_main_iteration ())
    ;
473
  iteration_done = old_done;
474 475 476 477 478 479 480 481 482 483 484 485

  if (quit_functions)
    {
      GList *reinvoke_list = NULL;
      GtkQuitFunction *quitf;

      while (quit_functions)
	{
	  quitf = quit_functions->data;

	  quit_functions = g_list_remove_link (quit_functions, quit_functions);

486 487
	  if ((quitf->main_level && quitf->main_level != main_level) ||
	      gtk_quit_invoke_function (quitf))
488
	    {
489
	      reinvoke_list = g_list_prepend (reinvoke_list, quitf);
490 491 492
	    }
	  else
	    {
493 494
	      g_list_free (tmp_list);
	      gtk_quit_destroy (quitf);
495 496 497 498 499 500 501 502 503 504 505 506 507 508
	    }
	}
      if (reinvoke_list)
	{
	  GList *tmp_list;
	  
	  tmp_list = g_list_last (reinvoke_list);
	  if (quit_functions)
	    quit_functions->prev = tmp_list;
	  tmp_list->next = quit_functions;
	  quit_functions = tmp_list;
	}
    }
	      
509 510 511 512 513 514 515
  main_level--;
}

guint
gtk_main_level (void)
{
  return main_level;
Elliot Lee's avatar
Elliot Lee committed
516 517 518
}

void
519
gtk_main_quit (void)
Elliot Lee's avatar
Elliot Lee committed
520
{
521
  iteration_done = TRUE;
Elliot Lee's avatar
Elliot Lee committed
522 523
}

Owen Taylor's avatar
Owen Taylor committed
524 525 526
gint
gtk_events_pending (void)
{
527 528 529 530 531 532 533 534 535
  gint result = 0;
  
  /* if this function is called from a timeout which will only return
   * if gtk needs processor time, we need to take iteration_done==TRUE
   * into account as well.
   */
  result = iteration_done;
  result += next_event != NULL;
  result += gdk_events_pending();
536

537 538
  result += current_idles != NULL;
  result += current_timeouts != NULL;
539

540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
  if (!result)
    {
      result += (idle_functions &&
		 (((GtkIdleFunction *)idle_functions->data)->priority <=
		  GTK_PRIORITY_INTERNAL));
    }
  
  if (!result && timeout_functions)
    {
      guint32 the_time;
      GtkTimeoutFunction *timeoutf;
      
      the_time = gdk_time_get ();
      
      timeoutf = timeout_functions->data;
      
      result += timeoutf->interval <= (the_time - timeoutf->start);
    }
  
559
  return result;
Owen Taylor's avatar
Owen Taylor committed
560 561 562
}

gint 
563
gtk_main_iteration (void)
564
{
Owen Taylor's avatar
Owen Taylor committed
565
  return gtk_main_iteration_do (TRUE);
566 567
}

Elliot Lee's avatar
Elliot Lee committed
568
gint
569
gtk_main_iteration_do (gboolean blocking)
Elliot Lee's avatar
Elliot Lee committed
570 571 572
{
  GtkWidget *event_widget;
  GtkWidget *grab_widget;
573 574
  GdkEvent *event = NULL;
  GList *tmp_list;
575
  
576
  iteration_done = FALSE;
577
  
Elliot Lee's avatar
Elliot Lee committed
578
  /* If this is a recursive call, and there are pending timeouts or
579 580 581
   * idles, finish them, then return immediately to avoid blocking
   * in gdk_event_get()
   */
Elliot Lee's avatar
Elliot Lee committed
582 583 584
  if (current_timeouts)
    {
      gtk_handle_current_timeouts( gdk_time_get());
585 586 587 588

      if (iteration_done)
	gdk_flush ();

589
      return iteration_done;
Elliot Lee's avatar
Elliot Lee committed
590 591 592
    }
  if (current_idles)
    {
593
      gtk_handle_current_idles ();
594 595 596 597

      if (iteration_done)
	gdk_flush ();

598
      return iteration_done;
Elliot Lee's avatar
Elliot Lee committed
599
    }
600
  
601
  /* If there is a valid event in 'next_event' then move it to 'event'
Elliot Lee's avatar
Elliot Lee committed
602
   */
603
  if (next_event)
Elliot Lee's avatar
Elliot Lee committed
604
    {
605 606
      event = next_event;
      next_event = NULL;
Elliot Lee's avatar
Elliot Lee committed
607
    }
608
  
Elliot Lee's avatar
Elliot Lee committed
609 610
  /* If we don't have an event then get one.
   */
611
  if (!event)
Elliot Lee's avatar
Elliot Lee committed
612 613 614 615 616 617 618 619
    {
      /* Handle setting of the "gdk" timer. If there are no
       *  timeout functions, then the timer is turned off.
       *  If there are timeout functions, then the timer is
       *  set to the shortest timeout interval (which is
       *  the first timeout function).
       */
      gtk_handle_timer ();
620
      
Owen Taylor's avatar
Owen Taylor committed
621
      if (blocking) event = gdk_event_get ();
Elliot Lee's avatar
Elliot Lee committed
622
    }
623
  
Elliot Lee's avatar
Elliot Lee committed
624 625 626 627
  /* "gdk_event_get" can return FALSE if the timer goes off
   *  and no events are pending. Therefore, we should make
   *  sure that we got an event before continuing.
   */
628
  if (event)
Elliot Lee's avatar
Elliot Lee committed
629 630 631 632
    {
      /* If there are any events pending then get the next one.
       */
      if (gdk_events_pending () > 0)
633
	next_event = gdk_event_get ();
634
      
Elliot Lee's avatar
Elliot Lee committed
635 636 637 638 639 640 641 642
      /* Try to compress enter/leave notify events. These event
       *  pairs occur when the mouse is dragged quickly across
       *  a window with many buttons (or through a menu). Instead
       *  of highlighting and de-highlighting each widget that
       *  is crossed it is better to simply de-highlight the widget
       *  which contained the mouse initially and highlight the
       *  widget which ends up containing the mouse.
       */
643 644 645 646 647 648 649 650 651
      if (next_event)
	if (((event->type == GDK_ENTER_NOTIFY) ||
	     (event->type == GDK_LEAVE_NOTIFY)) &&
	    ((next_event->type == GDK_ENTER_NOTIFY) ||
	     (next_event->type == GDK_LEAVE_NOTIFY)) &&
	    (next_event->type != event->type) &&
	    (next_event->any.window == event->any.window))
	  {
	    gdk_event_free (event);
652 653
	    gdk_event_free (next_event);
	    next_event = NULL;
654 655
	    
	    goto event_handling_done;
656
	  }
657
      
Elliot Lee's avatar
Elliot Lee committed
658 659
      /* Find the widget which got the event. We store the widget
       *  in the user_data field of GdkWindow's.
660 661 662 663
       *  Ignore the event if we don't have a widget for it, except
       *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
       *  Though this happens rarely, bogus events can occour
       *  for e.g. destroyed GdkWindows. 
Elliot Lee's avatar
Elliot Lee committed
664
       */
665
      event_widget = gtk_get_event_widget (event);
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
      if (!event_widget)
	{
	  /* To handle selection INCR transactions, we select
	   * PropertyNotify events on the requestor window and create
	   * a corresponding (fake) GdkWindow so that events get
	   * here. There won't be a widget though, so we have to handle
	   * them specially
	   */
	  if (event->type == GDK_PROPERTY_NOTIFY)
	    gtk_selection_incr_event (event->any.window,
				      &event->property);
	  
	  gdk_event_free (event);
	  
	  goto event_handling_done;
	}
      
      /* Push the event onto a stack of current events for
       * gtk_current_event_get().
       */
      current_events = g_list_prepend (current_events, event);
687
      
Elliot Lee's avatar
Elliot Lee committed
688 689 690 691 692
      /* If there is a grab in effect...
       */
      if (grabs)
	{
	  grab_widget = grabs->data;
693
	  
Elliot Lee's avatar
Elliot Lee committed
694 695 696 697
	  /* If the grab widget is an ancestor of the event widget
	   *  then we send the event to the original event widget.
	   *  This is the key to implementing modality.
	   */
698
	  if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
699
	      gtk_widget_is_ancestor (event_widget, grab_widget))
Elliot Lee's avatar
Elliot Lee committed
700 701 702 703 704 705
	    grab_widget = event_widget;
	}
      else
	{
	  grab_widget = event_widget;
	}
Elliot Lee's avatar
Elliot Lee committed
706

Elliot Lee's avatar
Elliot Lee committed
707 708 709 710 711 712 713
      /* Not all events get sent to the grabbing widget.
       * The delete, destroy, expose, focus change and resize
       *  events still get sent to the event widget because
       *  1) these events have no meaning for the grabbing widget
       *  and 2) redirecting these events to the grabbing widget
       *  could cause the display to be messed up.
       */
714
      switch (event->type)
Elliot Lee's avatar
Elliot Lee committed
715 716 717
	{
	case GDK_NOTHING:
	  break;
718
	  
Elliot Lee's avatar
Elliot Lee committed
719
	case GDK_DELETE:
720
	  gtk_widget_ref (event_widget);
721 722
	  if (!gtk_widget_event (event_widget, event) &&
	      !GTK_OBJECT_DESTROYED (event_widget))
723
	    gtk_widget_destroy (event_widget);
724
	  gtk_widget_unref (event_widget);
Elliot Lee's avatar
Elliot Lee committed
725
	  break;
726
	  
Elliot Lee's avatar
Elliot Lee committed
727
	case GDK_DESTROY:
728
	  gtk_widget_ref (event_widget);
729
	  gtk_widget_event (event_widget, event);
730 731
	  if (!GTK_OBJECT_DESTROYED (event_widget))
	    gtk_widget_destroy (event_widget);
732
	  gtk_widget_unref (event_widget);
Elliot Lee's avatar
Elliot Lee committed
733
	  break;
734
	  
Elliot Lee's avatar
Elliot Lee committed
735 736
	case GDK_PROPERTY_NOTIFY:
	case GDK_EXPOSE:
737
	case GDK_NO_EXPOSE:
Elliot Lee's avatar
Elliot Lee committed
738 739 740 741 742 743 744 745
	case GDK_FOCUS_CHANGE:
	case GDK_CONFIGURE:
	case GDK_MAP:
	case GDK_UNMAP:
	case GDK_SELECTION_CLEAR:
	case GDK_SELECTION_REQUEST:
	case GDK_SELECTION_NOTIFY:
	case GDK_CLIENT_EVENT:
Elliot Lee's avatar
Elliot Lee committed
746 747 748 749 750
	case GDK_DRAG_BEGIN:
	case GDK_DRAG_REQUEST:
	case GDK_DROP_ENTER:
	case GDK_DROP_LEAVE:
	case GDK_DROP_DATA_AVAIL:
751
	case GDK_VISIBILITY_NOTIFY:
752
	  gtk_widget_event (event_widget, event);
Elliot Lee's avatar
Elliot Lee committed
753
	  break;
754
	  
755 756 757 758 759 760 761 762
	case GDK_KEY_PRESS:
	case GDK_KEY_RELEASE:
	  if (key_snoopers)
	    {
	      if (gtk_invoke_key_snoopers (grab_widget, event))
		break;
	    }
	  /* else fall through */
Elliot Lee's avatar
Elliot Lee committed
763 764 765 766 767 768 769 770
	case GDK_MOTION_NOTIFY:
	case GDK_BUTTON_PRESS:
	case GDK_2BUTTON_PRESS:
	case GDK_3BUTTON_PRESS:
	case GDK_BUTTON_RELEASE:
	case GDK_PROXIMITY_IN:
	case GDK_PROXIMITY_OUT:
	case GDK_OTHER_EVENT:
771
	  gtk_propagate_event (grab_widget, event);
Elliot Lee's avatar
Elliot Lee committed
772
	  break;
773
	  
Elliot Lee's avatar
Elliot Lee committed
774
	case GDK_ENTER_NOTIFY:
775
	  if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
776 777 778
	    {
	      gtk_widget_event (grab_widget, event);
	      if (event_widget == grab_widget)
779
		GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
780 781
	    }
	  break;
782
	  
783
	case GDK_LEAVE_NOTIFY:
784
	  if (GTK_WIDGET_LEAVE_PENDING (event_widget))
785
	    {
786
	      GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
787 788
	      gtk_widget_event (event_widget, event);
	    }
789
	  else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
790
	    gtk_widget_event (grab_widget, event);
Elliot Lee's avatar
Elliot Lee committed
791 792
	  break;
	}
793 794 795 796
      
      tmp_list = current_events;
      current_events = g_list_remove_link (current_events, tmp_list);
      g_list_free_1 (tmp_list);
797
      
798
      gdk_event_free (event);
Elliot Lee's avatar
Elliot Lee committed
799 800 801
    }
  else
    {
802 803
      if (gdk_events_pending() == 0)
	gtk_handle_idle ();
Elliot Lee's avatar
Elliot Lee committed
804
    }
805
  
806 807 808
event_handling_done:
  
  /* Handle timeout functions that may have expired.
Elliot Lee's avatar
Elliot Lee committed
809 810
   */
  gtk_handle_timeouts ();
811
  
812 813 814
  if (iteration_done)
    gdk_flush ();
  
815
  return iteration_done;
Elliot Lee's avatar
Elliot Lee committed
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
}

gint
gtk_true (void)
{
  return TRUE;
}

gint
gtk_false (void)
{
  return FALSE;
}

void
gtk_grab_add (GtkWidget *widget)
{
833
  g_return_if_fail (widget != NULL);
834
  
835
  if (!GTK_WIDGET_HAS_GRAB (widget))
836 837
    {
      GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
838
      
839
      grabs = g_slist_prepend (grabs, widget);
840
      gtk_widget_ref (widget);
841
    }
Elliot Lee's avatar
Elliot Lee committed
842 843
}

844 845 846 847 848 849 850 851
GtkWidget*
gtk_grab_get_current (void)
{
  if (grabs)
    return GTK_WIDGET (grabs->data);
  return NULL;
}

Elliot Lee's avatar
Elliot Lee committed
852 853 854
void
gtk_grab_remove (GtkWidget *widget)
{
855
  g_return_if_fail (widget != NULL);
856
  
857 858 859
  if (GTK_WIDGET_HAS_GRAB (widget))
    {
      GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
860
      
861
      grabs = g_slist_remove (grabs, widget);
862
      gtk_widget_unref (widget);
863
    }
Elliot Lee's avatar
Elliot Lee committed
864 865 866 867
}

void
gtk_init_add (GtkFunction function,
868
	      gpointer	  data)
Elliot Lee's avatar
Elliot Lee committed
869 870
{
  GtkInitFunction *init;
871
  
Elliot Lee's avatar
Elliot Lee committed
872 873 874
  init = g_new (GtkInitFunction, 1);
  init->function = function;
  init->data = data;
875
  
Elliot Lee's avatar
Elliot Lee committed
876 877 878
  init_functions = g_list_prepend (init_functions, init);
}

879
guint
880
gtk_key_snooper_install (GtkKeySnoopFunc snooper,
881
			 gpointer	 func_data)
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
{
  GtkKeySnooperData *data;
  static guint snooper_id = 1;

  g_return_val_if_fail (snooper != NULL, 0);

  data = g_new (GtkKeySnooperData, 1);
  data->func = snooper;
  data->func_data = func_data;
  data->id = snooper_id++;
  key_snoopers = g_slist_prepend (key_snoopers, data);

  return data->id;
}

void
898
gtk_key_snooper_remove (guint		 snooper_id)
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
{
  GtkKeySnooperData *data = NULL;
  GSList *slist;

  slist = key_snoopers;
  while (slist)
    {
      data = slist->data;
      if (data->id == snooper_id)
	break;

      slist = slist->next;
      data = NULL;
    }
  if (data)
    key_snoopers = g_slist_remove (key_snoopers, data);
}

static gint
gtk_invoke_key_snoopers (GtkWidget *grab_widget,
			 GdkEvent  *event)
{
  GSList *slist;
  gint return_val = FALSE;

  slist = key_snoopers;
  while (slist && !return_val)
    {
      GtkKeySnooperData *data;

      data = slist->data;
      slist = slist->next;
      return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
    }

  return return_val;
}

937 938 939
guint
gtk_timeout_add_full (guint32		 interval,
		      GtkFunction	 function,
940
		      GtkCallbackMarshal marshal,
941 942
		      gpointer		 data,
		      GtkDestroyNotify	 destroy)
Elliot Lee's avatar
Elliot Lee committed
943
{
944
  static guint timeout_tag = 1;
Elliot Lee's avatar
Elliot Lee committed
945
  GtkTimeoutFunction *timeoutf;
946
  
947 948
  g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);

Elliot Lee's avatar
Elliot Lee committed
949 950 951 952 953 954
  /* Create a new timeout function structure.
   * The start time is the current time.
   */
  if (!timeout_mem_chunk)
    timeout_mem_chunk = g_mem_chunk_new ("timeout mem chunk", sizeof (GtkTimeoutFunction),
					 1024, G_ALLOC_AND_FREE);
955
  
Elliot Lee's avatar
Elliot Lee committed
956
  timeoutf = g_chunk_new (GtkTimeoutFunction, timeout_mem_chunk);
957
  
Elliot Lee's avatar
Elliot Lee committed
958 959 960 961 962
  timeoutf->tag = timeout_tag++;
  timeoutf->start = gdk_time_get ();
  timeoutf->interval = interval;
  timeoutf->originterval = interval;
  timeoutf->function = function;
963
  timeoutf->marshal = marshal;
Elliot Lee's avatar
Elliot Lee committed
964 965
  timeoutf->data = data;
  timeoutf->destroy = destroy;
966
  
Elliot Lee's avatar
Elliot Lee committed
967
  gtk_timeout_insert (timeoutf);
968
  
Elliot Lee's avatar
Elliot Lee committed
969 970 971 972 973 974 975 976 977 978 979
  return timeoutf->tag;
}

static void
gtk_timeout_destroy (GtkTimeoutFunction *timeoutf)
{
  if (timeoutf->destroy)
    (timeoutf->destroy) (timeoutf->data);
  g_mem_chunk_free (timeout_mem_chunk, timeoutf);
}

980
guint
Elliot Lee's avatar
Elliot Lee committed
981 982 983 984
gtk_timeout_add (guint32     interval,
		 GtkFunction function,
		 gpointer    data)
{
985
  return gtk_timeout_add_full (interval, function, FALSE, data, NULL);
Elliot Lee's avatar
Elliot Lee committed
986 987
}

988
guint
989
gtk_timeout_add_interp (guint32		   interval,
Elliot Lee's avatar
Elliot Lee committed
990
			GtkCallbackMarshal function,
991
			gpointer	   data,
Elliot Lee's avatar
Elliot Lee committed
992 993
			GtkDestroyNotify   destroy)
{
994
  return gtk_timeout_add_full (interval, NULL, function, data, destroy);
Elliot Lee's avatar
Elliot Lee committed
995 996
}

997 998 999 1000 1001 1002
/* Search for the specified tag in a list of timeouts. If it
 * is found, destroy the timeout, and either remove the link
 * or set link->data to NULL depending on remove_link
 */
static gint
gtk_timeout_remove_from_list (GList **list, guint tag, gint remove_link)
Elliot Lee's avatar
Elliot Lee committed
1003 1004
{
  GList *tmp_list;
1005
  GtkTimeoutFunction *timeoutf;
1006

1007
  tmp_list = *list;
Elliot Lee's avatar
Elliot Lee committed
1008 1009 1010 1011 1012 1013
  while (tmp_list)
    {
      timeoutf = tmp_list->data;
      
      if (timeoutf->tag == tag)
	{
1014 1015 1016 1017 1018 1019 1020 1021
	  if (remove_link)
	    {
	      *list = g_list_remove_link (*list, tmp_list);
	      g_list_free (tmp_list);
	    }
	  else
	    tmp_list->data = NULL;

Elliot Lee's avatar
Elliot Lee committed
1022 1023
	  gtk_timeout_destroy (timeoutf);
	  
1024
	  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1025 1026 1027 1028
	}
      
      tmp_list = tmp_list->next;
    }
1029 1030 1031 1032 1033 1034
  return FALSE;
}

void
gtk_timeout_remove (guint tag)
{
1035
  
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
  /* Remove a timeout function.
   * (Which, basically, involves searching the
   *  list for the tag).
   */

  if (gtk_timeout_remove_from_list (&timeout_functions, tag, TRUE))
    return;
  if (gtk_timeout_remove_from_list (&current_timeouts, tag, TRUE))
    return;
  if (gtk_timeout_remove_from_list (&running_timeouts, tag, FALSE))
    return;
Elliot Lee's avatar
Elliot Lee committed
1047 1048
}

1049 1050 1051 1052
/* We rely on some knowledge of how g_list_insert_sorted works to make
 * sure that we insert at the _end_ of the idles of this priority
 */
static gint
1053
gtk_idle_compare (gconstpointer a, gconstpointer b)
1054
{
1055 1056
  return (((const GtkIdleFunction *)a)->priority <
	  ((const GtkIdleFunction *)b)->priority)
1057 1058 1059
    ? -1 : 1;
}