gtkmain.c 47 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* 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
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser 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 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27 28
#include "gdkconfig.h"

29
#include <locale.h>
Robert Brady's avatar
Robert Brady committed
30 31 32 33 34

#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
#include <libintl.h>
#endif

Elliot Lee's avatar
Elliot Lee committed
35 36
#include <stdio.h>
#include <stdlib.h>
37
#include <string.h>
38
#include <gmodule.h>
39 40
#ifdef G_OS_UNIX
#include <unistd.h>
41
#include <sys/types.h>		/* For uid_t, gid_t */
42
#endif
Tor Lillqvist's avatar
Tor Lillqvist committed
43 44 45 46 47
#ifdef G_OS_WIN32
#define STRICT
#include <windows.h>
#undef STRICT
#endif
48 49 50

#include <pango/pango-utils.h>	/* For pango_split_file_list */

51
#include "gtkaccelmap.h"
52
#include "gtkdnd.h"
53
#include "gtkversion.h"
Elliot Lee's avatar
Elliot Lee committed
54 55 56
#include "gtkmain.h"
#include "gtkrc.h"
#include "gtkselection.h"
57
#include "gtksettings.h"
Elliot Lee's avatar
Elliot Lee committed
58 59 60
#include "gtksignal.h"
#include "gtkwidget.h"
#include "gtkwindow.h"
61
#include "gtkprivate.h"
62
#include "config.h"
63
#include "gtkdebug.h"
Owen Taylor's avatar
Owen Taylor committed
64
#include "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
65 66 67

/* Private type definitions
 */
68 69
typedef struct _GtkInitFunction		 GtkInitFunction;
typedef struct _GtkQuitFunction		 GtkQuitFunction;
70
typedef struct _GtkClosure	         GtkClosure;
71
typedef struct _GtkKeySnooperData	 GtkKeySnooperData;
Elliot Lee's avatar
Elliot Lee committed
72 73 74 75 76 77 78

struct _GtkInitFunction
{
  GtkFunction function;
  gpointer data;
};

79 80
struct _GtkQuitFunction
{
81
  guint id;
82 83 84 85 86 87 88
  guint main_level;
  GtkCallbackMarshal marshal;
  GtkFunction function;
  gpointer data;
  GtkDestroyNotify destroy;
};

89
struct _GtkClosure
Elliot Lee's avatar
Elliot Lee committed
90
{
91
  GtkCallbackMarshal marshal;
Elliot Lee's avatar
Elliot Lee committed
92 93 94 95
  gpointer data;
  GtkDestroyNotify destroy;
};

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

103 104 105 106
static gint  gtk_quit_invoke_function	 (GtkQuitFunction    *quitf);
static void  gtk_quit_destroy		 (GtkQuitFunction    *quitf);
static gint  gtk_invoke_key_snoopers	 (GtkWidget	     *grab_widget,
					  GdkEvent	     *event);
107 108 109 110 111 112 113

static void     gtk_destroy_closure      (gpointer            data);
static gboolean gtk_invoke_idle_timeout  (gpointer            data);
static void     gtk_invoke_input         (gpointer            data,
					  gint                source,
					  GdkInputCondition   condition);

Manish Singh's avatar
Manish Singh committed
114
#if 0
115 116 117 118
static void  gtk_error			 (gchar		     *str);
static void  gtk_warning		 (gchar		     *str);
static void  gtk_message		 (gchar		     *str);
static void  gtk_print			 (gchar		     *str);
Manish Singh's avatar
Manish Singh committed
119
#endif
120

121 122
static GtkWindowGroup *gtk_main_get_window_group (GtkWidget   *widget);

123 124 125
const guint gtk_major_version = GTK_MAJOR_VERSION;
const guint gtk_minor_version = GTK_MINOR_VERSION;
const guint gtk_micro_version = GTK_MICRO_VERSION;
126 127
const guint gtk_binary_age = GTK_BINARY_AGE;
const guint gtk_interface_age = GTK_INTERFACE_AGE;
128

Tim Janik's avatar
Tim Janik committed
129
static guint gtk_main_loop_level = 0;
130
static gint gtk_initialized = FALSE;
131
static GList *current_events = NULL;
Elliot Lee's avatar
Elliot Lee committed
132

133 134
static GSList *main_loops = NULL;      /* stack of currently executing main loops */

135
static GList *init_functions = NULL;	   /* A list of init functions.
Elliot Lee's avatar
Elliot Lee committed
136
					    */
137 138 139
static GList *quit_functions = NULL;	   /* A list of quit functions.
					    */
static GMemChunk *quit_mem_chunk = NULL;
Elliot Lee's avatar
Elliot Lee committed
140

141 142
static GSList *key_snoopers = NULL;

143
static GdkVisual *gtk_visual;		   /* The visual to be used in creating new
Elliot Lee's avatar
Elliot Lee committed
144 145
					    *  widgets.
					    */
146
static GdkColormap *gtk_colormap;	   /* The colormap to be used in creating new
Elliot Lee's avatar
Elliot Lee committed
147 148 149
					    *  widgets.
					    */

150
guint gtk_debug_flags = 0;		   /* Global GTK debug flag */
151 152

#ifdef G_ENABLE_DEBUG
153
static const GDebugKey gtk_debug_keys[] = {
154
  {"misc", GTK_DEBUG_MISC},
155
  {"plugsocket", GTK_DEBUG_PLUGSOCKET},
156
  {"text", GTK_DEBUG_TEXT},
157 158
  {"tree", GTK_DEBUG_TREE},
  {"updates", GTK_DEBUG_UPDATES}
159
};
160

161
static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
162

163
#endif /* G_ENABLE_DEBUG */
Elliot Lee's avatar
Elliot Lee committed
164

165 166 167 168 169 170
gchar*
gtk_check_version (guint required_major,
		   guint required_minor,
		   guint required_micro)
{
  if (required_major > GTK_MAJOR_VERSION)
171
    return "Gtk+ version too old (major mismatch)";
172
  if (required_major < GTK_MAJOR_VERSION)
173
    return "Gtk+ version too new (major mismatch)";
174
  if (required_minor > GTK_MINOR_VERSION)
175
    return "Gtk+ version too old (minor mismatch)";
176
  if (required_minor < GTK_MINOR_VERSION)
177
    return "Gtk+ version too new (minor mismatch)";
178
  if (required_micro < GTK_MICRO_VERSION - GTK_BINARY_AGE)
179
    return "Gtk+ version too new (micro mismatch)";
180
  if (required_micro > GTK_MICRO_VERSION)
181
    return "Gtk+ version too old (micro mismatch)";
182 183 184
  return NULL;
}

185 186
#undef gtk_init_check

Owen Taylor's avatar
Owen Taylor committed
187 188 189 190 191 192 193 194 195 196
/* This checks to see if the process is running suid or sgid
 * at the current time. If so, we don't allow GTK+ to be initialized.
 * This is meant to be a mild check - we only error out if we
 * can prove the programmer is doing something wrong, not if
 * they could be doing something wrong. For this reason, we
 * don't use issetugid() on BSD or prctl (PR_GET_DUMPABLE).
 */
static gboolean
check_setugid (void)
{
197
/* this isn't at all relevant on MS Windows and doesn't compile ... --hb */
Hans Breuer's avatar
Hans Breuer committed
198
#ifndef G_OS_WIN32
Owen Taylor's avatar
Owen Taylor committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
  gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
  
#ifdef HAVE_GETRESUID
  /* These aren't in the header files, so we prototype them here.
   */
  int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
  int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);

  if (getresuid (&ruid, &euid, &suid) != 0 ||
      getresgid (&rgid, &egid, &sgid) != 0)
#endif /* HAVE_GETRESUID */
    {
      suid = ruid = getuid ();
      sgid = rgid = getgid ();
      euid = geteuid ();
      egid = getegid ();
    }

  if (ruid != euid || ruid != suid ||
      rgid != egid || rgid != sgid)
    {
      g_warning ("This process is currently running setuid or setgid.\n"
		 "This is not a supported use of GTK+. You must create a helper\n"
		 "program instead. For further details, see:\n\n"
		 "    http://www.gtk.org/setuid.html\n\n"
		 "Refusing to initialize GTK+.");
      exit (1);
    }
Hans Breuer's avatar
Hans Breuer committed
228
#endif
Owen Taylor's avatar
Owen Taylor committed
229 230 231
  return TRUE;
}

Tor Lillqvist's avatar
Tor Lillqvist committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
#ifdef G_OS_WIN32

G_WIN32_DLLMAIN_FOR_DLL_NAME(static, dll_name)

const gchar *
_gtk_get_libdir (void)
{
  static char *gtk_libdir = NULL;
  if (gtk_libdir == NULL)
    gtk_libdir = g_win32_get_package_installation_subdirectory
      (GETTEXT_PACKAGE, dll_name, "lib");

  return gtk_libdir;
}

const gchar *
_gtk_get_localedir (void)
{
  static char *gtk_localedir = NULL;
  if (gtk_localedir == NULL)
    gtk_localedir = g_win32_get_package_installation_subdirectory
      (GETTEXT_PACKAGE, dll_name, "lib\\locale");

  return gtk_localedir;
}

const gchar *
_gtk_get_sysconfdir (void)
{
  static char *gtk_sysconfdir = NULL;
  if (gtk_sysconfdir == NULL)
    gtk_sysconfdir = g_win32_get_package_installation_subdirectory
      (GETTEXT_PACKAGE, dll_name, "etc");

  return gtk_sysconfdir;
}

const gchar *
_gtk_get_data_prefix (void)
{
  static char *gtk_data_prefix = NULL;
  if (gtk_data_prefix == NULL)
    gtk_data_prefix = g_win32_get_package_installation_directory
      (GETTEXT_PACKAGE, dll_name);

  return gtk_data_prefix;
}

#endif /* G_OS_WIN32 */

282 283 284
static gchar **
get_module_path (void)
{
285
  const gchar *module_path_env = g_getenv ("GTK_MODULE_PATH");
Matthias Clasen's avatar
Matthias Clasen committed
286
  const gchar *exe_prefix = g_getenv ("GTK_EXE_PREFIX");
287
  gchar **result;
288
  gchar *module_path;
289 290 291 292 293
  gchar *default_dir;

  if (exe_prefix)
    default_dir = g_build_filename (exe_prefix, "lib", "gtk-2.0", "modules", NULL);
  else
Tor Lillqvist's avatar
Tor Lillqvist committed
294 295
    default_dir = g_build_filename (GTK_LIBDIR, "gtk-2.0", "modules", NULL);

296 297
  module_path = g_strconcat (module_path_env ? module_path_env : "",
			     module_path_env ? G_SEARCHPATH_SEPARATOR_S : "",
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
			     default_dir, NULL);

  result = pango_split_file_list (module_path);

  g_free (default_dir);
  g_free (module_path);

  return result;
}

static GModule *
find_module (gchar      **module_path,
	     const gchar *name)
{
  GModule *module;
  gchar *module_name;
  gint i;

  if (g_path_is_absolute (name))
    return g_module_open (name, G_MODULE_BIND_LAZY);

  for (i = 0; module_path[i]; i++)
    {
      gchar *version_directory;

      version_directory = g_build_filename (module_path[i], GTK_BINARY_VERSION, NULL);
      module_name = g_module_build_path (version_directory, name);
      g_free (version_directory);
      
      if (g_file_test (module_name, G_FILE_TEST_EXISTS))
	{
329
	  module = g_module_open (module_name, G_MODULE_BIND_LAZY);
330
	  g_free (module_name);
331
	  return module;
332 333 334 335 336 337 338 339
	}
      
      g_free (module_name);

      module_name = g_module_build_path (module_path[i], name);
      
      if (g_file_test (module_name, G_FILE_TEST_EXISTS))
	{
Hans Breuer's avatar
Hans Breuer committed
340
	  module = g_module_open (module_name, G_MODULE_BIND_LAZY);
341
	  g_free (module_name);
Hans Breuer's avatar
Hans Breuer committed
342
	  return module;
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
	}

      g_free (module_name);
    }

  /* As last resort, try loading without an absolute path (using system
   * library path)
   */
  module_name = g_module_build_path (NULL, name);
  module = g_module_open (module_name, G_MODULE_BIND_LAZY);
  g_free(module_name);

  return module;
}

static GSList *
load_module (GSList      *gtk_modules,
	     gchar      **module_path,
	     const gchar *name)
{
  GtkModuleInitFunc modinit_func = NULL;
  GModule *module = NULL;
  
  if (g_module_supported ())
    {
      module = find_module (module_path, name);
      if (module &&
370
	  g_module_symbol (module, "gtk_module_init", (gpointer *) &modinit_func) &&
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
	  modinit_func)
	{
	  if (!g_slist_find (gtk_modules, modinit_func))
	    {
	      g_module_make_resident (module);
	      gtk_modules = g_slist_prepend (gtk_modules, modinit_func);
	    }
	  else
	    {
	      g_module_close (module);
	      module = NULL;
	    }
	}
    }
  if (!modinit_func)
    {
      g_message ("Failed to load module \"%s\": %s",
		 module ? g_module_name (module) : name,
		 g_module_error ());
      if (module)
	g_module_close (module);
    }
  
  return gtk_modules;
}

static GSList *
load_modules (const char *module_str)
{
  gchar **module_path = get_module_path ();
  gchar **module_names = pango_split_file_list (module_str);
  GSList *gtk_modules = NULL;
  gint i;
  
  for (i = 0; module_names[i]; i++)
    gtk_modules = load_module (gtk_modules, module_path, module_names[i]);
  
  gtk_modules = g_slist_reverse (gtk_modules);
  
  g_strfreev (module_names);
  g_strfreev (module_path);

  return gtk_modules;
}

416 417 418 419 420 421
static gboolean do_setlocale = TRUE;

/**
 * gtk_disable_setlocale:
 * 
 * Prevents gtk_init() and gtk_init_check() from automatically
422 423 424 425
 * calling <literal>setlocale (LC_ALL, "")</literal>. You would 
 * want to use this function if you wanted to set the locale for 
 * your program to something other than the user's locale, or if 
 * you wanted to set different values for different locale categories.
426 427 428
 *
 * Most programs should not need to call this function.
 **/
429
void
430 431 432 433 434 435 436 437
gtk_disable_setlocale (void)
{
  if (gtk_initialized)
    g_warning ("gtk_disable_setlocale() must be called before gtk_init()");
    
  do_setlocale = FALSE;
}

438 439 440
gboolean
gtk_init_check (int	 *argc,
		char   ***argv)
Elliot Lee's avatar
Elliot Lee committed
441
{
442
  GString *gtk_modules_string = NULL;
443 444
  GSList *gtk_modules = NULL;
  GSList *slist;
445
  const gchar *env_string;
446

Tim Janik's avatar
Tim Janik committed
447
  if (gtk_initialized)
448
    return TRUE;
Tim Janik's avatar
Tim Janik committed
449

Owen Taylor's avatar
Owen Taylor committed
450 451 452
  if (!check_setugid ())
    return FALSE;
  
453 454 455 456 457 458
#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
459 460 461

  if (do_setlocale)
    setlocale (LC_ALL, "");
462
  
463 464
  /* Initialize "gdk". We pass along the 'argc' and 'argv'
   *  parameters as they contain information that GDK uses
Elliot Lee's avatar
Elliot Lee committed
465
   */
466 467
  if (!gdk_init_check (argc, argv))
    return FALSE;
468

Owen Taylor's avatar
Owen Taylor committed
469
  gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
470
  
471
#ifdef G_ENABLE_DEBUG
Matthias Clasen's avatar
Matthias Clasen committed
472
  env_string = g_getenv ("GTK_DEBUG");
473 474 475
  if (env_string != NULL)
    {
      gtk_debug_flags = g_parse_debug_string (env_string,
476 477
					      gtk_debug_keys,
					      gtk_ndebug_keys);
478 479
      env_string = NULL;
    }
480
#endif	/* G_ENABLE_DEBUG */
481

Matthias Clasen's avatar
Matthias Clasen committed
482
  env_string = g_getenv ("GTK_MODULES");
483
  if (env_string)
484
    gtk_modules_string = g_string_new (env_string);
485

486 487
  if (argc && argv)
    {
Owen Taylor's avatar
Owen Taylor committed
488
      gint i, j, k;
489 490 491
      
      for (i = 1; i < *argc;)
	{
492 493
	  if (strcmp ("--gtk-module", (*argv)[i]) == 0 ||
	      strncmp ("--gtk-module=", (*argv)[i], 13) == 0)
494
	    {
495
	      gchar *module_name = (*argv)[i] + 12;
496
	      
497 498
	      if (*module_name == '=')
		module_name++;
499
	      else if (i + 1 < *argc)
500 501 502 503 504 505
		{
		  (*argv)[i] = NULL;
		  i += 1;
		  module_name = (*argv)[i];
		}
	      (*argv)[i] = NULL;
506

507
	      if (module_name && *module_name)
508 509 510 511 512 513 514 515
		{
		  if (gtk_modules_string)
		    g_string_append_c (gtk_modules_string, G_SEARCHPATH_SEPARATOR);
		  else
		    gtk_modules_string = g_string_new (NULL);

		  g_string_append (gtk_modules_string, module_name);
		}
516
	    }
517 518
	  else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
	    {
519
	      GLogLevelFlags fatal_mask;
520
	      
521 522 523
	      fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
	      fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
              g_log_set_always_fatal (fatal_mask);
524 525
	      (*argv)[i] = NULL;
	    }
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
#ifdef G_ENABLE_DEBUG
	  else if ((strcmp ("--gtk-debug", (*argv)[i]) == 0) ||
		   (strncmp ("--gtk-debug=", (*argv)[i], 12) == 0))
	    {
	      gchar *equal_pos = strchr ((*argv)[i], '=');
	      
	      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])
		{
		  gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
							   gtk_debug_keys,
							   gtk_ndebug_keys);
		  (*argv)[i] = NULL;
		  i += 1;
		}
	      (*argv)[i] = NULL;
	    }
	  else if ((strcmp ("--gtk-no-debug", (*argv)[i]) == 0) ||
		   (strncmp ("--gtk-no-debug=", (*argv)[i], 15) == 0))
	    {
	      gchar *equal_pos = strchr ((*argv)[i], '=');
	      
	      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])
		{
		  gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
							    gtk_debug_keys,
							    gtk_ndebug_keys);
		  (*argv)[i] = NULL;
		  i += 1;
		}
	      (*argv)[i] = NULL;
	    }
#endif /* G_ENABLE_DEBUG */
570 571
	  i += 1;
	}
572
      
Owen Taylor's avatar
Owen Taylor committed
573 574 575 576 577 578 579 580 581 582 583 584 585 586
      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;
	    }
	}
587
    }
588 589 590

  if (gtk_debug_flags & GTK_DEBUG_UPDATES)
    gdk_window_set_debug_updates (TRUE);
591

592
  /* load gtk modules */
593
  if (gtk_modules_string)
594
    {
595 596
      gtk_modules = load_modules (gtk_modules_string->str);
      g_string_free (gtk_modules_string, TRUE);
597 598
    }

Owen Taylor's avatar
Owen Taylor committed
599
#ifdef ENABLE_NLS
600
  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
Robert Brady's avatar
Robert Brady committed
601
#    ifdef HAVE_BIND_TEXTDOMAIN_CODESET
602
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
603
#    endif
Owen Taylor's avatar
Owen Taylor committed
604 605
#endif  

606 607
  {
  /* Translate to default:RTL if you want your widgets
Robert Brady's avatar
Robert Brady committed
608 609 610
   * to be RTL, otherwise translate to default:LTR.
   * Do *not* translate it to "predefinito:LTR", if it
   * it isn't default:LTR or default:RTL it will not work 
611 612 613 614 615 616 617 618 619
   */
    char *e = _("default:LTR");
    if (strcmp (e, "default:RTL")==0) {
      gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
    } else if (strcmp (e, "default:LTR")) {
      g_warning ("Whoever translated default:LTR did so wrongly.\n");
    }
  }

Elliot Lee's avatar
Elliot Lee committed
620 621 622 623 624 625
  /* 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 ();
626

627
  gtk_type_init (0);
628
  _gtk_accel_map_init ();  
629
  _gtk_rc_init ();
630
  
Elliot Lee's avatar
Elliot Lee committed
631 632
  /* Set the 'initialized' flag.
   */
633
  gtk_initialized = TRUE;
634

635
  /* initialize gtk modules
636
   */
637
  for (slist = gtk_modules; slist; slist = slist->next)
638
    {
639
      if (slist->data)
640 641
	{
	  GtkModuleInitFunc modinit;
642
	  
643 644 645 646
	  modinit = slist->data;
	  modinit (argc, argv);
	}
    }
647
  g_slist_free (gtk_modules);
648
  
649 650
  return TRUE;
}
651 652 653

#undef gtk_init

654 655 656 657 658 659
void
gtk_init (int *argc, char ***argv)
{
  if (!gtk_init_check (argc, argv))
    {
      g_warning ("cannot open display: %s", gdk_get_display ());
660
      exit (1);
661
    }
Elliot Lee's avatar
Elliot Lee committed
662 663
}

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
#ifdef G_OS_WIN32

static void
check_sizeof_GtkWindow (size_t sizeof_GtkWindow)
{
  if (sizeof_GtkWindow != sizeof (GtkWindow))
    g_error ("Incompatible build!\n"
	     "The code using GTK+ thinks GtkWindow is of different\n"
             "size than it actually is in this build of GTK+.\n"
	     "On Windows, this probably means that you have compiled\n"
	     "your code with gcc without the -fnative-struct switch.");
}

/* These two functions might get more checks added later, thus pass
 * in the number of extra args.
 */
void
gtk_init_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow)
{
  check_sizeof_GtkWindow (sizeof_GtkWindow);
  gtk_init (argc, argv);
}

gboolean
gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow)
{
  check_sizeof_GtkWindow (sizeof_GtkWindow);
  return gtk_init_check (argc, argv);
}

#endif

Elliot Lee's avatar
Elliot Lee committed
696
void
Tor Lillqvist's avatar
Tor Lillqvist committed
697
gtk_exit (gint errorcode)
Elliot Lee's avatar
Elliot Lee committed
698 699 700
{
  /* Only if "gtk" has been initialized should we de-initialize.
   */
701
  gdk_exit (errorcode);
Elliot Lee's avatar
Elliot Lee committed
702 703
}

704 705 706 707

/**
 * gtk_set_locale:
 *
708 709 710
 * Initializes internationalization support for GTK+. gtk_init()
 * automatically does this, so there is typically no point
 * in calling this function.
711
 *
712 713 714 715 716
 * If you are calling this function because you changed the locale
 * after GTK+ is was initialized, then calling this function
 * may help a bit. (Note, however, that changing the locale
 * after GTK+ is initialized may produce inconsistent results and
 * is not really supported.)
717
 * 
718
 * In detail - sets the current locale according to the
719 720 721
 * program environment. This is the same as calling the C library function
 * <literal>setlocale (LC_ALL, "")</literal> but also takes care of the 
 * locale specific setup of the windowing system used by GDK.
722
 * 
723
 * Return value: a string corresponding to the locale set, as with the
724
 * C library function <function>setlocale()</function>.
725
 **/
Elliot Lee's avatar
Elliot Lee committed
726
gchar*
727
gtk_set_locale (void)
Elliot Lee's avatar
Elliot Lee committed
728 729 730 731
{
  return gdk_set_locale ();
}

732 733 734 735 736 737 738 739 740 741 742
/**
 * gtk_get_default_language:
 *
 * Returns the ISO language code for the default language currently in
 * effect. (Note that this can change over the life of an
 * application.)  The default language is derived from the current
 * locale. It determines, for example, whether GTK+ uses the
 * right-to-left or left-to-right text direction.
 * 
 * Return value: the default language as an allocated string, must be freed
 **/
743
PangoLanguage *
744 745 746
gtk_get_default_language (void)
{
  gchar *lang;
747
  PangoLanguage *result;
748 749 750 751 752 753 754 755 756 757
  gchar *p;
  
  lang = g_strdup (setlocale (LC_CTYPE, NULL));
  p = strchr (lang, '.');
  if (p)
    *p = '\0';
  p = strchr (lang, '@');
  if (p)
    *p = '\0';

758 759 760
  result = pango_language_from_string (lang);
  g_free (lang);
  
Hans Breuer's avatar
Hans Breuer committed
761
  return result;
762 763
}

Elliot Lee's avatar
Elliot Lee committed
764
void
765
gtk_main (void)
Elliot Lee's avatar
Elliot Lee committed
766 767 768 769
{
  GList *tmp_list;
  GList *functions;
  GtkInitFunction *init;
770 771
  GMainLoop *loop;

Tim Janik's avatar
Tim Janik committed
772
  gtk_main_loop_level++;
773
  
774 775 776
  loop = g_main_new (TRUE);
  main_loops = g_slist_prepend (main_loops, loop);

Elliot Lee's avatar
Elliot Lee committed
777 778
  tmp_list = functions = init_functions;
  init_functions = NULL;
779
  
Elliot Lee's avatar
Elliot Lee committed
780 781 782 783
  while (tmp_list)
    {
      init = tmp_list->data;
      tmp_list = tmp_list->next;
784
      
Elliot Lee's avatar
Elliot Lee committed
785 786 787 788
      (* init->function) (init->data);
      g_free (init);
    }
  g_list_free (functions);
789

790 791 792 793 794 795 796
  if (g_main_is_running (main_loops->data))
    {
      GDK_THREADS_LEAVE ();
      g_main_run (loop);
      GDK_THREADS_ENTER ();
      gdk_flush ();
    }
797 798 799 800 801 802 803 804 805 806

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

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

807
	  tmp_list = quit_functions;
808
	  quit_functions = g_list_remove_link (quit_functions, quit_functions);
809
	  g_list_free_1 (tmp_list);
810

Tim Janik's avatar
Tim Janik committed
811
	  if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
812
	      gtk_quit_invoke_function (quitf))
813
	    {
814
	      reinvoke_list = g_list_prepend (reinvoke_list, quitf);
815 816 817
	    }
	  else
	    {
818
	      gtk_quit_destroy (quitf);
819 820 821 822
	    }
	}
      if (reinvoke_list)
	{
Tim Janik's avatar
Tim Janik committed
823
	  GList *work;
824
	  
Tim Janik's avatar
Tim Janik committed
825
	  work = g_list_last (reinvoke_list);
826
	  if (quit_functions)
Tim Janik's avatar
Tim Janik committed
827 828 829
	    quit_functions->prev = work;
	  work->next = quit_functions;
	  quit_functions = work;
830
	}
831 832

      gdk_flush ();
833 834
    }
	      
835 836 837 838
  main_loops = g_slist_remove (main_loops, loop);

  g_main_destroy (loop);

Tim Janik's avatar
Tim Janik committed
839
  gtk_main_loop_level--;
840 841 842 843 844
}

guint
gtk_main_level (void)
{
Tim Janik's avatar
Tim Janik committed
845
  return gtk_main_loop_level;
Elliot Lee's avatar
Elliot Lee committed
846 847 848
}

void
849
gtk_main_quit (void)
Elliot Lee's avatar
Elliot Lee committed
850
{
851 852
  g_return_if_fail (main_loops != NULL);

853
  g_main_quit (main_loops->data);
Elliot Lee's avatar
Elliot Lee committed
854 855
}

856 857 858
gint
gtk_events_pending (void)
{
859 860 861
  gboolean result;
  
  GDK_THREADS_LEAVE ();  
862
  result = g_main_pending ();
863 864 865
  GDK_THREADS_ENTER ();

  return result;
866 867
}

868
gboolean
869
gtk_main_iteration (void)
870
{
871
  GDK_THREADS_LEAVE ();
872
  g_main_iteration (TRUE);
873
  GDK_THREADS_ENTER ();
874

875 876
  if (main_loops)
    return !g_main_is_running (main_loops->data);
877 878
  else
    return TRUE;
879 880
}

881
gboolean
882
gtk_main_iteration_do (gboolean blocking)
883
{
884
  GDK_THREADS_LEAVE ();
885
  g_main_iteration (blocking);
886
  GDK_THREADS_ENTER ();
887

888 889
  if (main_loops)
    return !g_main_is_running (main_loops->data);
890 891
  else
    return TRUE;
892 893
}

Owen Taylor's avatar
Owen Taylor committed
894
void 
895
gtk_main_do_event (GdkEvent *event)
Elliot Lee's avatar
Elliot Lee committed
896 897 898
{
  GtkWidget *event_widget;
  GtkWidget *grab_widget;
899
  GtkWindowGroup *window_group;
900
  GdkEvent *next_event;
901
  GList *tmp_list;
902 903 904

  /* If there are any events pending then get the next one.
   */
905
  next_event = gdk_event_peek ();
906
  
907 908 909 910 911 912 913
  /* 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.
914
   */
915 916 917 918 919 920 921 922
  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))
      {
923 924 925 926
	/* Throw both the peeked copy and the queued copy away 
	 */
	gdk_event_free (next_event);
	next_event = gdk_event_get ();
927 928 929 930
	gdk_event_free (next_event);
	
	return;
      }
931

932
  if (next_event)
933
    gdk_event_free (next_event);
934 935 936 937 938 939 940 941 942 943

  /* Find the widget which got the event. We store the widget
   *  in the user_data field of GdkWindow's.
   *  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. 
   */
  event_widget = gtk_get_event_widget (event);
  if (!event_widget)
Elliot Lee's avatar
Elliot Lee committed
944
    {
945 946 947 948 949 950 951 952 953
      /* 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);
954 955 956
      else if (event->type == GDK_SETTING)
	_gtk_settings_handle_event (&event->setting);

957
      return;
Elliot Lee's avatar
Elliot Lee committed
958
    }
959
  
960 961
  /* Push the event onto a stack of current events for
   * gtk_current_event_get().
Elliot Lee's avatar
Elliot Lee committed
962
   */
963
  current_events = g_list_prepend (current_events, event);
964 965

  window_group = gtk_main_get_window_group (event_widget);
966
  
967
  /* If there is a grab in effect...
Elliot Lee's avatar
Elliot Lee committed
968
   */
969
  if (window_group->grabs)
Elliot Lee's avatar
Elliot Lee committed
970
    {
971
      grab_widget = window_group->grabs->data;
972
      
973 974 975 976 977 978 979 980 981 982 983
      /* 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.
       */
      if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
	  gtk_widget_is_ancestor (event_widget, grab_widget))
	grab_widget = event_widget;
    }
  else
    {
      grab_widget = event_widget;
Elliot Lee's avatar
Elliot Lee committed
984
    }
985

986 987 988 989 990 991 992 993 994
  /* 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.
   * 
   * Drag events are also not redirected, since it isn't
   *  clear what the semantics of that would be.
Elliot Lee's avatar
Elliot Lee committed
995
   */
996
  switch (event->type)
Elliot Lee's avatar
Elliot Lee committed
997
    {
998 999
    case GDK_NOTHING:
      break;
1000
      
1001 1002
    case GDK_DELETE:
      gtk_widget_ref (event_widget);
1003
      if ((!window_group->grabs || gtk_widget_get_toplevel (window_group->grabs->data) == event_widget) &&
Owen Taylor's avatar
Owen Taylor committed
1004
	  !gtk_widget_event (event_widget, event))
1005 1006 1007
	gtk_widget_destroy (event_widget);
      gtk_widget_unref (event_widget);
      break;
1008
      
1009
    case GDK_DESTROY:
1010 1011 1012 1013 1014 1015 1016
      /* Unexpected GDK_DESTROY from the outside, ignore for
       * child windows, handle like a GDK_DELETE for toplevels
       */
      if (!event_widget->parent)
	{
	  gtk_widget_ref (event_widget);
	  if (!gtk_widget_event (event_widget, event) &&
1017
	      GTK_WIDGET_REALIZED (event_widget))
1018 1019 1020
	    gtk_widget_destroy (event_widget);
	  gtk_widget_unref (event_widget);
	}
1021
      break;
1022
      
1023
    case GDK_EXPOSE:
1024
      if (event->any.window && GTK_WIDGET_DOUBLE_BUFFERED (event_widget))
1025 1026 1027 1028 1029 1030 1031
	{
	  gdk_window_begin_paint_region (event->any.window, event->expose.region);
	  gtk_widget_send_expose (event_widget, event);
	  gdk_window_end_paint (event->any.window);
	}
      else
	gtk_widget_send_expose (event_widget, event);
1032 1033 1034
      break;

    case GDK_PROPERTY_NOTIFY:
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
    case GDK_NO_EXPOSE:
    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:
    case GDK_VISIBILITY_NOTIFY:
1045
    case GDK_WINDOW_STATE:
1046 1047
      gtk_widget_event (event_widget, event);
      break;
1048

1049
    case GDK_SCROLL:
1050 1051 1052 1053 1054 1055
    case GDK_BUTTON_PRESS:
    case GDK_2BUTTON_PRESS:
    case GDK_3BUTTON_PRESS:
      gtk_propagate_event (grab_widget, event);
      break;

1056 1057 1058
    case GDK_KEY_PRESS:
    case GDK_KEY_RELEASE:
      if (key_snoopers)
Elliot Lee's avatar
Elliot Lee committed
1059
	{
1060 1061
	  if (gtk_invoke_key_snoopers (grab_widget, event))
	    break;
Elliot Lee's avatar
Elliot Lee committed
1062
	}
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
      /* else fall through */
    case GDK_MOTION_NOTIFY:
    case GDK_BUTTON_RELEASE:
    case GDK_PROXIMITY_IN:
    case GDK_PROXIMITY_OUT:
      gtk_propagate_event (grab_widget, event);
      break;
      
    case GDK_ENTER_NOTIFY:
      if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
Elliot Lee's avatar
Elliot Lee committed
1073
	{
1074 1075
	  g_object_ref (event_widget);
	  
1076 1077 1078
	  gtk_widget_event (grab_widget, event);
	  if (event_widget == grab_widget)
	    GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
1079 1080
	  
	  g_object_unref (event_widget);
Elliot Lee's avatar
Elliot Lee committed
1081
	}
1082 1083 1084 1085
      break;
      
    case GDK_LEAVE_NOTIFY:
      if (GTK_WIDGET_LEAVE_PENDING (event_widget))
Elliot Lee's avatar
Elliot Lee committed
1086
	{
1087
	  GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
1088
	  gtk_widget_event (event_widget, event);
Elliot Lee's avatar
Elliot Lee committed
1089
	}
1090 1091 1092
      else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
	gtk_widget_event (grab_widget, event);
      break;
1093
      
1094 1095
    case GDK_DRAG_STATUS:
    case GDK_DROP_FINISHED:
1096
      _gtk_drag_source_handle_event (event_widget, event);
1097 1098 1099 1100 1101
      break;
    case GDK_DRAG_ENTER:
    case GDK_DRAG_LEAVE:
    case GDK_DRAG_MOTION:
    case GDK_DROP_START:
1102
      _gtk_drag_dest_handle_event (event_widget, event);
1103
      break;
1104 1105 1106
    default:
      g_assert_not_reached ();
      break;
Elliot Lee's avatar
Elliot Lee committed
1107
    }
1108
  
1109 1110 1111
  tmp_list = current_events;
  current_events = g_list_remove_link (current_events, tmp_list);
  g_list_free_1 (tmp_list);
Elliot Lee's avatar
Elliot Lee committed
1112 1113
}

1114
gboolean
Elliot Lee's avatar
Elliot Lee committed
1115 1116 1117 1118 1119
gtk_true (void)
{
  return TRUE;
}

1120
gboolean
Elliot Lee's avatar
Elliot Lee committed
1121 1122 1123 1124 1125
gtk_false (void)
{
  return FALSE;
}

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
static GtkWindowGroup *
gtk_main_get_window_group (GtkWidget   *widget)
{
  GtkWidget *toplevel = NULL;

  if (widget)
    toplevel = gtk_widget_get_toplevel (widget);

  if (toplevel && GTK_IS_WINDOW (toplevel))
    return _gtk_window_get_group (GTK_WINDOW (toplevel));
  else
    return _gtk_window_get_group (NULL);
}

typedef struct
{
1142 1143
  GtkWidget *old_grab_widget;
  GtkWidget *new_grab_widget;
1144 1145
} GrabNotifyInfo;

1146 1147 1148 1149 1150 1151 1152
static gboolean
check_is_grabbed (GtkWidget *widget,
		  GtkWidget *grab_widget)
{
  if (grab_widget)
    return !(widget == grab_widget || gtk_widget_is_ancestor (widget, grab_widget));
  else
1153
    return FALSE;
1154 1155
}

1156 1157 1158 1159 1160 1161
static void
gtk_grab_notify_foreach (GtkWidget *child,
			 gpointer   data)
                        
{
  GrabNotifyInfo *info = data;
1162 1163
  gboolean was_grabbed = check_is_grabbed (child, info->old_grab_widget);
  gboolean is_grabbed = check_is_grabbed (child, info->new_grab_widget);
1164

1165
  if (was_grabbed != is_grabbed)
1166 1167
    {
      g_object_ref (G_OBJECT (child));
1168 1169 1170
      
      gtk_signal_emit_by_name (GTK_OBJECT (child), "grab_notify", was_grabbed);
      
1171
      if (GTK_IS_CONTAINER (child))
1172
	gtk_container_foreach (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
      
      g_object_unref (G_OBJECT (child));
    }
}

static void
gtk_grab_notify (GtkWindowGroup *group,
		 GtkWidget      *grab_widget,
		 gboolean        was_grabbed)
{
  GList *toplevels;
  GrabNotifyInfo info;

1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
  if (was_grabbed)
    {
      info.old_grab_widget = grab_widget;
      info.new_grab_widget = group->grabs ? group->grabs->data : NULL;
    }
  else
    {
      info.old_grab_widget = (group->grabs && group->grabs->next) ? group->grabs->next->data : NULL;
      info.new_grab_widget = grab_widget;
    }
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207

  g_object_ref (group);
  g_object_ref (grab_widget);

  toplevels = gtk_window_list_toplevels ();
  g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
			    
  while (toplevels)
    {
      GtkWindow *toplevel = toplevels->data;
      toplevels = g_list_delete_link (toplevels, toplevels);

1208
      if (group == _gtk_window_get_group (toplevel))
1209 1210 1211 1212 1213 1214 1215 1216
	gtk_container_foreach (GTK_CONTAINER (toplevel), gtk_grab_notify_foreach, &info);
      g_object_unref (toplevel);
    }

  g_object_unref (group);
  g_object_unref (grab_widget);
}

Elliot Lee's avatar
Elliot Lee committed
1217 1218 1219
void
gtk_grab_add (GtkWidget *widget)
{
1220 1221 1222
  GtkWindowGroup *group;
  gboolean was_grabbed;
  
1223
  g_return_if_fail (widget != NULL);
1224
  
1225
  if (!GTK_WIDGET_HAS_GRAB (widget) && GTK_WIDGET_IS_SENSITIVE (widget))
1226 1227
    {
      GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
1228
      
1229 1230 1231 1232
      group = gtk_main_get_window_group (widget);

      was_grabbed = (group->grabs != NULL);
      
1233
      gtk_widget_ref (widget);
1234 1235
      group->grabs = g_slist_prepend (group->grabs, widget);

1236
      gtk_grab_notify (group, widget, FALSE);
1237
    }
Elliot Lee's avatar
Elliot Lee committed
1238 1239
}

1240 1241 1242
GtkWidget*
gtk_grab_get_current (void)
{
1243 1244 1245 1246 1247 1248
  GtkWindowGroup *group;

  group = gtk_main_get_window_group (NULL);

  if (group->grabs)
    return GTK_WIDGET (group->grabs->data);
1249 1250 1251
  return NULL;
}

Elliot Lee's avatar
Elliot Lee committed
1252 1253 1254
void
gtk_grab_remove (GtkWidget *widget)
{
1255 1256
  GtkWindowGroup *group;
  
1257
  g_return_if_fail (widget != NULL);
1258
  
1259 1260 1261
  if (GTK_WIDGET_HAS_GRAB (widget))
    {
      GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);