2 /* gtktoolkit.c -- Native portion of GtkToolkit
3 Copyright (C) 1998, 1999, 2005, 2007 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
41 #include "gnu_java_awt_peer_gtk_GtkToolkit.h"
45 #define RC_FILE ".classpath-gtkrc"
47 /* From java.awt.SystemColor */
49 #define AWT_ACTIVE_CAPTION 1
50 #define AWT_ACTIVE_CAPTION_TEXT 2
51 #define AWT_ACTIVE_CAPTION_BORDER 3
52 #define AWT_INACTIVE_CAPTION 4
53 #define AWT_INACTIVE_CAPTION_TEXT 5
54 #define AWT_INACTIVE_CAPTION_BORDER 6
56 #define AWT_WINDOW_BORDER 8
57 #define AWT_WINDOW_TEXT 9
59 #define AWT_MENU_TEXT 11
61 #define AWT_TEXT_TEXT 13
62 #define AWT_TEXT_HIGHLIGHT 14
63 #define AWT_TEXT_HIGHLIGHT_TEXT 15
64 #define AWT_TEXT_INACTIVE_TEXT 16
65 #define AWT_CONTROL 17
66 #define AWT_CONTROL_TEXT 18
67 #define AWT_CONTROL_HIGHLIGHT 19
68 #define AWT_CONTROL_LT_HIGHLIGHT 20
69 #define AWT_CONTROL_SHADOW 21
70 #define AWT_CONTROL_DK_SHADOW 22
71 #define AWT_SCROLLBAR 23
73 #define AWT_INFO_TEXT 25
74 #define AWT_NUM_COLORS 26
79 #define VK_CAPS_LOCK 20
82 static jclass gtkgenericpeer
;
83 static jclass gtktoolkit
;
84 static JavaVM
*java_vm
;
85 static jmethodID printCurrentThreadID
;
86 static jmethodID setRunningID
;
89 * The global AWT lock object.
91 static jobject global_lock
;
103 g_assert((*java_vm
)->GetEnv(java_vm
, &tmp
.void_env
, JNI_VERSION_1_2
) == JNI_OK
);
108 GtkWindowGroup
*cp_gtk_global_window_group
;
109 double cp_gtk_dpi_conversion_factor
;
111 static void jni_lock_cb();
112 static void jni_unlock_cb();
113 static void init_glib_threads(JNIEnv
*, jint
, jobject
);
114 static gboolean
post_set_running_flag (gpointer
);
115 static gboolean
set_running_flag (gpointer
);
116 static gboolean
clear_running_flag (gpointer
);
117 static void init_dpi_conversion_factor (void);
118 static void dpi_changed_cb (GtkSettings
*settings
,
121 #if GTK_MINOR_VERSION > 4
122 static GLogFunc old_glog_func
;
123 static void glog_func (const gchar
*log_domain
,
124 GLogLevelFlags log_level
,
125 const gchar
*message
,
129 JNIEXPORT
void JNICALL
130 Java_gnu_java_awt_peer_gtk_GtkToolkit_initIDs
131 (JNIEnv
*env
, jclass cls
__attribute__((unused
)))
133 gtkpeer_init_pointer_IDs(env
);
137 * Call gtk_init. It is very important that this happen before any other
140 * The portableNativeSync argument may have the values:
141 * 1 if the Java property gnu.classpath.awt.gtk.portable.native.sync
143 * 0 if it is set to "false"
148 JNIEXPORT
void JNICALL
149 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv
*env
,
150 jclass clazz
__attribute__((unused
)),
151 jint portableNativeSync
,
156 char *homedir
, *rcpath
= NULL
;
158 gtkgenericpeer
= (*env
)->FindClass(env
, "gnu/java/awt/peer/gtk/GtkGenericPeer");
160 gtkgenericpeer
= (*env
)->NewGlobalRef(env
, gtkgenericpeer
);
162 printCurrentThreadID
= (*env
)->GetStaticMethodID (env
, gtkgenericpeer
,
163 "printCurrentThread", "()V");
165 g_assert((*env
)->GetJavaVM(env
, &java_vm
) == 0);
167 /* GTK requires a program's argc and argv variables, and requires that they
168 be valid. Set it up. */
169 argv
= (char **) g_malloc (sizeof (char *) * 2);
170 argv
[0] = (char *) g_malloc(1);
174 init_glib_threads(env
, portableNativeSync
, lock
);
176 /* From GDK 2.0 onwards we have to explicitly call gdk_threads_init */
179 gtk_init (&argc
, &argv
);
182 XSynchronize (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), True
);
185 gtk_widget_set_default_colormap (gdk_rgb_get_colormap ());
187 /* Make sure queued calls don't get sent to GTK/GDK while
188 we're shutting down. */
189 atexit (gdk_threads_enter
);
191 if ((homedir
= getenv ("HOME")))
193 rcpath
= (char *) g_malloc (strlen (homedir
) + strlen (RC_FILE
) + 2);
194 sprintf (rcpath
, "%s/%s", homedir
, RC_FILE
);
197 gtk_rc_parse ((rcpath
) ? rcpath
: RC_FILE
);
203 /* On errors or warning print a whole stacktrace. */
204 #if GTK_MINOR_VERSION > 4
205 old_glog_func
= g_log_set_default_handler (&glog_func
, NULL
);
208 cp_gtk_button_init_jni (env
);
209 cp_gtk_checkbox_init_jni ();
210 cp_gtk_choice_init_jni ();
211 cp_gtk_component_init_jni ();
212 cp_gtk_filedialog_init_jni ();
213 cp_gtk_list_init_jni ();
214 cp_gtk_menuitem_init_jni ();
215 cp_gtk_scrollbar_init_jni ();
216 cp_gtk_textcomponent_init_jni ();
217 cp_gtk_window_init_jni ();
219 cp_gtk_global_window_group
= gtk_window_group_new ();
221 init_dpi_conversion_factor ();
223 gtktoolkit
= (*env
)->FindClass(env
, "gnu/java/awt/peer/gtk/GtkMainThread");
224 setRunningID
= (*env
)->GetStaticMethodID (env
, gtktoolkit
,
225 "setRunning", "(Z)V");
229 * A callback function that implements gdk_threads_enter(). This is
230 * implemented to wrap the JNI MonitorEnter() function.
232 static void jni_lock_cb()
234 JNIEnv
* env
= cp_gtk_gdk_env();
235 if ((*env
)->MonitorEnter(env
, global_lock
) != JNI_OK
)
237 printf("failure while entering GTK monitor\n");
242 * A callback function that implements gdk_threads_leave(). This is
243 * implemented to wrap the JNI MonitorExit() function.
245 static void jni_unlock_cb()
248 JNIEnv
* env
= cp_gtk_gdk_env();
249 if ((*env
)->MonitorExit(env
, global_lock
))
251 printf("failure while exiting GTK monitor\n");
255 /** Initialize GLIB's threads properly, based on the value of the
256 gnu.classpath.awt.gtk.portable.native.sync Java system property. If
257 that's unset, use the PORTABLE_NATIVE_SYNC config.h macro. (TODO:
258 In some release following 0.10, that config.h macro will go away.)
261 init_glib_threads(JNIEnv
*env
, jint portableNativeSync
, jobject lock
)
263 if (portableNativeSync
< 0)
265 #ifdef PORTABLE_NATIVE_SYNC /* Default value, if not set by the Java system
267 portableNativeSync
= 1;
269 portableNativeSync
= 0;
273 if (!g_thread_supported ())
275 if (portableNativeSync
)
277 global_lock
= (*env
)->NewGlobalRef(env
, lock
);
278 gdk_threads_set_lock_functions(&jni_lock_cb
, &jni_unlock_cb
);
284 /* Warn if portable native sync is desired but the threading
285 system is already initialized. In that case we can't
286 override the threading implementation with our portable
287 native sync functions. */
288 if (portableNativeSync
)
289 g_printerr ("peer warning: portable native sync disabled.\n");
292 /* Debugging progress message; uncomment if needed: */
293 /* printf("called gthread init\n"); */
297 cp_gtk_print_current_thread (void)
299 (*cp_gtk_gdk_env())->CallStaticVoidMethod (cp_gtk_gdk_env(), gtkgenericpeer
, printCurrentThreadID
);
302 /* This is a big hack, needed until this pango bug is resolved:
303 http://bugzilla.gnome.org/show_bug.cgi?id=119081.
304 See: http://mail.gnome.org/archives/gtk-i18n-list/2003-August/msg00001.html
307 init_dpi_conversion_factor ()
309 GtkSettings
*settings
= gtk_settings_get_default ();
312 klass
= G_OBJECT_CLASS (GTK_SETTINGS_GET_CLASS (settings
));
313 if (g_object_class_find_property (klass
, "gtk-xft-dpi"))
316 g_object_get (settings
, "gtk-xft-dpi", &int_dpi
, NULL
);
317 /* If int_dpi == -1 gtk-xft-dpi returns the default value. So we
318 have to do approximate calculation here. */
320 cp_gtk_dpi_conversion_factor
= PANGO_SCALE
* 72.0 / 96.;
322 cp_gtk_dpi_conversion_factor
=
323 PANGO_SCALE
* 72.0 / (int_dpi
/ PANGO_SCALE
);
325 g_signal_connect (settings
, "notify::gtk-xft-dpi",
326 G_CALLBACK (dpi_changed_cb
), NULL
);
330 cp_gtk_dpi_conversion_factor
= PANGO_SCALE
* 72.0 / 96.;
334 dpi_changed_cb (GtkSettings
*settings
,
335 GParamSpec
*pspec
__attribute__((unused
)))
338 g_object_get (settings
, "gtk-xft-dpi", &int_dpi
, NULL
);
340 cp_gtk_dpi_conversion_factor
= PANGO_SCALE
* 72.0 / 96.;
342 cp_gtk_dpi_conversion_factor
=
343 PANGO_SCALE
* 72.0 / (int_dpi
/ PANGO_SCALE
);
346 #if GTK_MINOR_VERSION > 4
348 glog_func (const gchar
*log_domain
,
349 GLogLevelFlags log_level
,
350 const gchar
*message
,
353 old_glog_func (log_domain
, log_level
, message
, user_data
);
354 if (log_level
& (G_LOG_LEVEL_ERROR
355 | G_LOG_LEVEL_CRITICAL
356 | G_LOG_LEVEL_WARNING
))
358 JNIEnv
*env
= cp_gtk_gdk_env ();
359 jthrowable
*exc
= (*env
)->ExceptionOccurred(env
);
360 gchar
*detail
= g_strconcat (log_domain
, ": ", message
, NULL
);
361 JCL_ThrowException (env
, "java/lang/InternalError", detail
);
363 (*env
)->ExceptionDescribe (env
);
365 (*env
)->Throw (env
, exc
);
367 (*env
)->ExceptionClear (env
);
372 JNIEXPORT
void JNICALL
373 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkMain
374 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
378 gtk_init_add (post_set_running_flag
, NULL
);
379 gtk_quit_add (gtk_main_level (), clear_running_flag
, NULL
);
386 JNIEXPORT
void JNICALL
387 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkQuit
388 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
390 gdk_threads_enter ();
394 gdk_threads_leave ();
398 static jint
gdk_color_to_java_color (GdkColor color
);
401 JNIEXPORT
void JNICALL
402 Java_gnu_java_awt_peer_gtk_GtkToolkit_beep
403 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
405 gdk_threads_enter ();
409 gdk_threads_leave ();
412 JNIEXPORT
void JNICALL
413 Java_gnu_java_awt_peer_gtk_GtkToolkit_sync
414 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
416 gdk_threads_enter ();
420 gdk_threads_leave ();
423 JNIEXPORT
void JNICALL
424 Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenSizeDimensions
425 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)),
428 jint
*dims
= (*env
)->GetIntArrayElements (env
, jdims
, 0);
430 gdk_threads_enter ();
432 dims
[0] = gdk_screen_width ();
433 dims
[1] = gdk_screen_height ();
435 gdk_threads_leave ();
437 (*env
)->ReleaseIntArrayElements(env
, jdims
, dims
, 0);
440 JNIEXPORT jint JNICALL
441 Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenResolution
442 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
446 gdk_threads_enter ();
448 res
= gdk_screen_width () / (gdk_screen_width_mm () / 25.4);
450 gdk_threads_leave ();
456 * Report the number of mouse buttons
457 * Returns the number of buttons of the first mouse found, or -1 if no mouse
458 * seems to be connected.
460 JNIEXPORT jint JNICALL
461 Java_gnu_java_awt_peer_gtk_GtkToolkit_getMouseNumberOfButtons
462 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
468 gdk_threads_enter ();
470 /* FIXME: Why doesn't this return the correct number? */
471 devices
= gdk_devices_list();
473 while( res
== -1 && devices
!= NULL
)
475 d
= GDK_DEVICE( devices
->data
);
476 if( d
->source
== GDK_SOURCE_MOUSE
)
478 devices
= devices
->next
;
481 gdk_threads_leave ();
486 #define CONVERT(type, state) \
487 gdk_color_to_java_color (style->type[GTK_STATE_ ## state])
489 JNIEXPORT
void JNICALL
490 Java_gnu_java_awt_peer_gtk_GtkToolkit_loadSystemColors
491 (JNIEnv
*env
, jobject obj
__attribute__((unused
)),
497 /* FIXME: this was deadlocking so assume it is thread-safe for now;
498 we need to replace this call with a .properties file anyway. */
500 gdk_threads_enter ();
503 colors
= (*env
)->GetIntArrayElements (env
, jcolors
, 0);
505 style
= gtk_widget_get_default_style ();
507 colors
[AWT_DESKTOP
] = CONVERT (bg
, SELECTED
);
508 colors
[AWT_ACTIVE_CAPTION
] = CONVERT (bg
, SELECTED
);
509 colors
[AWT_ACTIVE_CAPTION_TEXT
] = CONVERT (text
, SELECTED
);
510 colors
[AWT_ACTIVE_CAPTION_BORDER
] = CONVERT (fg
, NORMAL
);
511 colors
[AWT_INACTIVE_CAPTION
] = CONVERT (base
, INSENSITIVE
);
512 colors
[AWT_INACTIVE_CAPTION_TEXT
] = CONVERT (fg
, INSENSITIVE
);
513 colors
[AWT_INACTIVE_CAPTION_BORDER
] = CONVERT (fg
, INSENSITIVE
);
514 colors
[AWT_WINDOW
] = CONVERT (bg
, NORMAL
);
515 colors
[AWT_WINDOW_BORDER
] = CONVERT (fg
, NORMAL
);
516 colors
[AWT_WINDOW_TEXT
] = CONVERT (fg
, NORMAL
);
517 colors
[AWT_MENU
] = CONVERT (bg
, NORMAL
);
518 colors
[AWT_MENU_TEXT
] = CONVERT (fg
, NORMAL
);
519 colors
[AWT_TEXT
] = CONVERT (bg
, NORMAL
);
520 colors
[AWT_TEXT_TEXT
] = CONVERT (fg
, NORMAL
);
521 colors
[AWT_TEXT_HIGHLIGHT
] = CONVERT (bg
, SELECTED
);
522 colors
[AWT_TEXT_HIGHLIGHT_TEXT
] = CONVERT (fg
, SELECTED
);
523 colors
[AWT_TEXT_INACTIVE_TEXT
] = CONVERT (bg
, INSENSITIVE
);
524 colors
[AWT_CONTROL
] = CONVERT (bg
, NORMAL
);
525 colors
[AWT_CONTROL_TEXT
] = CONVERT (fg
, NORMAL
);
526 colors
[AWT_CONTROL_HIGHLIGHT
] = CONVERT (base
, ACTIVE
);
527 colors
[AWT_CONTROL_LT_HIGHLIGHT
] = CONVERT (bg
, PRELIGHT
);
528 colors
[AWT_CONTROL_SHADOW
] = CONVERT (bg
, ACTIVE
);
529 colors
[AWT_CONTROL_DK_SHADOW
] = CONVERT (fg
, INSENSITIVE
);
530 colors
[AWT_SCROLLBAR
] = CONVERT (base
, INSENSITIVE
);
531 colors
[AWT_INFO
] = CONVERT (bg
, NORMAL
);
532 colors
[AWT_INFO_TEXT
] = CONVERT (fg
, NORMAL
);
534 (*env
)->ReleaseIntArrayElements(env
, jcolors
, colors
, 0);
537 gdk_threads_leave ();
544 gdk_color_to_java_color (GdkColor gdk_color
)
551 factor
= 255.0 / 65535.0;
553 red
= (float) gdk_color
.red
* factor
;
554 green
= (float) gdk_color
.green
* factor
;
555 blue
= (float) gdk_color
.blue
* factor
;
557 return (jint
) (0xff000000 | (red
<< 16) | (green
<< 8) | blue
);
560 JNIEXPORT jint JNICALL
561 Java_gnu_java_awt_peer_gtk_GtkToolkit_getLockState
562 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)),
566 GdkModifierType state
, mask
;
567 GdkWindow
*root_window
;
569 gdk_threads_enter ();
571 root_window
= gdk_get_default_root_window ();
572 gdk_window_get_pointer (root_window
, &coord
, &coord
, &state
);
577 mask
= GDK_SHIFT_MASK
;
580 mask
= GDK_CONTROL_MASK
;
583 /* This is dubious, since MOD1 could have been mapped to something
585 mask
= GDK_MOD1_MASK
;
587 #if GTK_CHECK_VERSION(2, 10, 0)
589 mask
= GDK_META_MASK
;
593 mask
= GDK_LOCK_MASK
;
599 gdk_threads_leave ();
604 return state
& mask
? 1 : 0;
608 post_set_running_flag (gpointer data
__attribute__((unused
)))
610 g_idle_add (set_running_flag
, NULL
);
615 set_running_flag (gpointer data
__attribute__((unused
)))
617 (*cp_gtk_gdk_env ())->CallStaticVoidMethod (cp_gtk_gdk_env (),
624 clear_running_flag (gpointer data
__attribute__((unused
)))
626 (*cp_gtk_gdk_env ())->CallStaticVoidMethod (cp_gtk_gdk_env (),
628 setRunningID
, FALSE
);