1 /* gtktoolkit.c -- Native portion of GtkToolkit
2 Copyright (C) 1998, 1999 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
40 #include "gnu_java_awt_peer_gtk_GtkToolkit.h"
41 #include "gthread-jni.h"
46 struct state_table
*native_state_table
;
47 struct state_table
*native_global_ref_table
;
50 jmethodID setBoundsCallbackID
;
52 jmethodID postActionEventID
;
53 jmethodID postMenuActionEventID
;
54 jmethodID postMouseEventID
;
55 jmethodID postConfigureEventID
;
56 jmethodID postExposeEventID
;
57 jmethodID postKeyEventID
;
58 jmethodID postFocusEventID
;
59 jmethodID postAdjustmentEventID
;
60 jmethodID postItemEventID
;
61 jmethodID choicePostItemEventID
;
62 jmethodID postListItemEventID
;
63 jmethodID postTextEventID
;
64 jmethodID postWindowEventID
;
65 jmethodID postInsetsChangedEventID
;
66 jmethodID windowGetWidthID
;
67 jmethodID windowGetHeightID
;
69 jmethodID beginNativeRepaintID
;
70 jmethodID endNativeRepaintID
;
72 jmethodID initComponentGraphicsID
;
73 jmethodID initComponentGraphics2DID
;
74 jmethodID setCursorID
;
88 g_assert((*java_vm
)->GetEnv(java_vm
, &tmp
.void_env
, JNI_VERSION_1_2
) == JNI_OK
);
93 GtkWindowGroup
*global_gtk_window_group
;
95 static void init_glib_threads(JNIEnv
*, jint
);
97 double dpi_conversion_factor
;
99 static void init_dpi_conversion_factor (void);
100 static void dpi_changed_cb (GtkSettings
*settings
,
104 * Call gtk_init. It is very important that this happen before any other
107 * The portableNativeSync argument may have the values:
108 * 1 if the Java property gnu.classpath.awt.gtk.portable.native.sync
110 * 0 if it is set to "false"
115 JNIEXPORT
void JNICALL
116 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv
*env
,
117 jclass clazz
__attribute__((unused
)),
118 jint portableNativeSync
)
122 char *homedir
, *rcpath
= NULL
;
124 jclass gtkgenericpeer
, gtkcomponentpeer
, gtkchoicepeer
, gtkwindowpeer
, gtkscrollbarpeer
, gtklistpeer
,
125 gtkmenuitempeer
, gtktextcomponentpeer
, window
, gdkgraphics
, gdkgraphics2d
;
127 gtkgenericpeer
= (*env
)->FindClass(env
, "gnu/java/awt/peer/gtk/GtkGenericPeer");
129 NSA_INIT (env
, gtkgenericpeer
);
131 g_assert((*env
)->GetJavaVM(env
, &java_vm
) == 0);
133 /* GTK requires a program's argc and argv variables, and requires that they
134 be valid. Set it up. */
135 argv
= (char **) g_malloc (sizeof (char *) * 2);
136 argv
[0] = (char *) g_malloc(1);
139 #else /* The following is a more efficient alternative, but less intuitively
140 * expresses what we are trying to do. This code is only run once, so
141 * I'm going for intuitive. */
146 init_glib_threads(env
, portableNativeSync
);
148 /* From GDK 2.0 onwards we have to explicitly call gdk_threads_init */
151 gtk_init (&argc
, &argv
);
154 gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
155 gtk_widget_set_default_visual (gdk_rgb_get_visual ());
157 /* Make sure queued calls don't get sent to GTK/GDK while
158 we're shutting down. */
159 atexit (gdk_threads_enter
);
161 gdk_event_handler_set ((GdkEventFunc
)awt_event_handler
, NULL
, NULL
);
163 if ((homedir
= getenv ("HOME")))
165 rcpath
= (char *) g_malloc (strlen (homedir
) + strlen (RC_FILE
) + 2);
166 sprintf (rcpath
, "%s/%s", homedir
, RC_FILE
);
169 gtk_rc_parse ((rcpath
) ? rcpath
: RC_FILE
);
175 /* setup cached IDs for posting GTK events to Java */
177 window
= (*env
)->FindClass (env
, "java/awt/Window");
179 gtkcomponentpeer
= (*env
)->FindClass (env
,
180 "gnu/java/awt/peer/gtk/GtkComponentPeer");
181 gtkchoicepeer
= (*env
)->FindClass (env
,
182 "gnu/java/awt/peer/gtk/GtkChoicePeer");
183 gtkwindowpeer
= (*env
)->FindClass (env
,
184 "gnu/java/awt/peer/gtk/GtkWindowPeer");
185 gtkscrollbarpeer
= (*env
)->FindClass (env
,
186 "gnu/java/awt/peer/gtk/GtkScrollbarPeer");
187 gtklistpeer
= (*env
)->FindClass (env
, "gnu/java/awt/peer/gtk/GtkListPeer");
188 gtkmenuitempeer
= (*env
)->FindClass (env
,
189 "gnu/java/awt/peer/gtk/GtkMenuItemPeer");
190 gtktextcomponentpeer
= (*env
)->FindClass (env
,
191 "gnu/java/awt/peer/gtk/GtkTextComponentPeer");
192 gdkgraphics
= (*env
)->FindClass (env
,
193 "gnu/java/awt/peer/gtk/GdkGraphics");
194 gdkgraphics2d
= (*env
)->FindClass (env
,
195 "gnu/java/awt/peer/gtk/GdkGraphics2D");
196 setBoundsCallbackID
= (*env
)->GetMethodID (env
, window
,
200 postMenuActionEventID
= (*env
)->GetMethodID (env
, gtkmenuitempeer
,
201 "postMenuActionEvent",
203 postMouseEventID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
204 "postMouseEvent", "(IJIIIIZ)V");
205 setCursorID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
207 beginNativeRepaintID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
208 "beginNativeRepaint", "()V");
210 endNativeRepaintID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
211 "endNativeRepaint", "()V");
213 postConfigureEventID
= (*env
)->GetMethodID (env
, gtkwindowpeer
,
214 "postConfigureEvent", "(IIII)V");
215 postWindowEventID
= (*env
)->GetMethodID (env
, gtkwindowpeer
,
217 "(ILjava/awt/Window;I)V");
218 postInsetsChangedEventID
= (*env
)->GetMethodID (env
, gtkwindowpeer
,
219 "postInsetsChangedEvent",
221 windowGetWidthID
= (*env
)->GetMethodID (env
, gtkwindowpeer
,
223 windowGetHeightID
= (*env
)->GetMethodID (env
, gtkwindowpeer
,
226 postExposeEventID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
227 "postExposeEvent", "(IIII)V");
228 postKeyEventID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
229 "postKeyEvent", "(IJIICI)V");
230 postFocusEventID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
231 "postFocusEvent", "(IZ)V");
232 postAdjustmentEventID
= (*env
)->GetMethodID (env
, gtkscrollbarpeer
,
233 "postAdjustmentEvent",
235 postItemEventID
= (*env
)->GetMethodID (env
, gtkcomponentpeer
,
237 "(Ljava/lang/Object;I)V");
238 choicePostItemEventID
= (*env
)->GetMethodID (env
, gtkchoicepeer
,
239 "choicePostItemEvent",
240 "(Ljava/lang/String;I)V");
241 postListItemEventID
= (*env
)->GetMethodID (env
, gtklistpeer
,
244 postTextEventID
= (*env
)->GetMethodID (env
, gtktextcomponentpeer
,
247 initComponentGraphicsID
= (*env
)->GetMethodID (env
, gdkgraphics
,
248 "initComponentGraphics",
250 initComponentGraphics2DID
= (*env
)->GetMethodID (env
, gdkgraphics2d
,
251 "initComponentGraphics2D",
253 global_gtk_window_group
= gtk_window_group_new ();
255 init_dpi_conversion_factor ();
259 /** Initialize GLIB's threads properly, based on the value of the
260 gnu.classpath.awt.gtk.portable.native.sync Java system property. If
261 that's unset, use the PORTABLE_NATIVE_SYNC config.h macro. (TODO:
262 In some release following 0.10, that config.h macro will go away.)
265 init_glib_threads(JNIEnv
*env
, jint portableNativeSync
)
267 if (portableNativeSync
< 0)
269 #ifdef PORTABLE_NATIVE_SYNC /* Default value, if not set by the Java system
271 portableNativeSync
= 1;
273 portableNativeSync
= 0;
277 (*env
)->GetJavaVM( env
, &the_vm
);
278 if (portableNativeSync
)
279 g_thread_init ( &portable_native_sync_jni_functions
);
281 g_thread_init ( NULL
);
283 /* Debugging progress message; uncomment if needed: */
284 /* printf("called gthread init\n"); */
288 /* This is a big hack, needed until this pango bug is resolved:
289 http://bugzilla.gnome.org/show_bug.cgi?id=119081.
290 See: http://mail.gnome.org/archives/gtk-i18n-list/2003-August/msg00001.html
293 init_dpi_conversion_factor ()
295 GtkSettings
*settings
= gtk_settings_get_default ();
298 klass
= G_OBJECT_CLASS (GTK_SETTINGS_GET_CLASS (settings
));
299 if (g_object_class_find_property (klass
, "gtk-xft-dpi"))
302 g_object_get (settings
, "gtk-xft-dpi", &int_dpi
, NULL
);
303 /* If int_dpi == -1 gtk-xft-dpi returns the default value. So we
304 have to do approximate calculation here. */
306 dpi_conversion_factor
= PANGO_SCALE
* 72.0 / 96.;
308 dpi_conversion_factor
= PANGO_SCALE
* 72.0 / (int_dpi
/ PANGO_SCALE
);
310 g_signal_connect (settings
, "notify::gtk-xft-dpi",
311 G_CALLBACK (dpi_changed_cb
), NULL
);
315 dpi_conversion_factor
= PANGO_SCALE
* 72.0 / 96.;
319 dpi_changed_cb (GtkSettings
*settings
,
320 GParamSpec
*pspec
__attribute__((unused
)))
323 g_object_get (settings
, "gtk-xft-dpi", &int_dpi
, NULL
);
325 dpi_conversion_factor
= PANGO_SCALE
* 72.0 / 96.;
327 dpi_conversion_factor
= PANGO_SCALE
* 72.0 / (int_dpi
/ PANGO_SCALE
);
331 within_human_latency_tolerance(struct timeval
*init
)
334 unsigned long milliseconds_elapsed
;
336 gettimeofday(&curr
, NULL
);
338 milliseconds_elapsed
= (((curr
.tv_sec
* 1000) + (curr
.tv_usec
/ 1000))
339 - ((init
->tv_sec
* 1000) + (init
->tv_usec
/ 1000)));
341 return milliseconds_elapsed
< 100;
345 JNIEXPORT
void JNICALL
346 Java_gnu_java_awt_peer_gtk_GtkToolkit_iterateNativeQueue
348 jobject self
__attribute__((unused
)),
352 /* We're holding an EventQueue lock, and we're about to acquire the GDK
353 * lock before dropping the EventQueue lock. This can deadlock if someone
354 * holds the GDK lock and wants to acquire the EventQueue lock; however
355 * all callbacks from GTK happen with the GDK lock released, so this
356 * would only happen in an odd case such as some JNI helper code
357 * acquiring the GDK lock and calling back into
358 * EventQueue.getNextEvent().
362 gettimeofday(&init
, NULL
);
364 gdk_threads_enter ();
365 (*env
)->MonitorExit (env
, lockedQueue
);
370 /* If we're blocking-when-empty, we want a do .. while loop. */
372 gtk_main_iteration ();
373 while (within_human_latency_tolerance (&init
)
374 && gtk_events_pending ());
378 /* If we're not blocking-when-empty, we want a while loop. */
379 while (within_human_latency_tolerance (&init
)
380 && gtk_events_pending ())
381 gtk_main_iteration ();
384 (*env
)->MonitorEnter (env
, lockedQueue
);
385 gdk_threads_leave ();
388 JNIEXPORT
void JNICALL
389 Java_gnu_java_awt_peer_gtk_GtkToolkit_wakeNativeQueue
390 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
392 g_main_context_wakeup (NULL
);
395 JNIEXPORT jboolean JNICALL
396 Java_gnu_java_awt_peer_gtk_GtkToolkit_nativeQueueEmpty
397 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
399 jboolean empty
= FALSE
;
400 gdk_threads_enter ();
401 empty
= ! gtk_events_pending();
402 gdk_threads_leave ();
407 static jint
gdk_color_to_java_color (GdkColor color
);
410 JNIEXPORT
void JNICALL
411 Java_gnu_java_awt_peer_gtk_GtkToolkit_beep
412 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
414 gdk_threads_enter ();
416 gdk_threads_leave ();
419 JNIEXPORT
void JNICALL
420 Java_gnu_java_awt_peer_gtk_GtkToolkit_sync
421 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
423 gdk_threads_enter ();
425 gdk_threads_leave ();
428 JNIEXPORT
void JNICALL
429 Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenSizeDimensions
430 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)),
433 jint
*dims
= (*env
)->GetIntArrayElements (env
, jdims
, 0);
435 gdk_threads_enter ();
437 dims
[0] = gdk_screen_width ();
438 dims
[1] = gdk_screen_height ();
440 gdk_threads_leave ();
442 (*env
)->ReleaseIntArrayElements(env
, jdims
, dims
, 0);
445 JNIEXPORT jint JNICALL
446 Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenResolution
447 (JNIEnv
*env
__attribute__((unused
)), jobject obj
__attribute__((unused
)))
451 gdk_threads_enter ();
453 res
= gdk_screen_width () / (gdk_screen_width_mm () / 25.4);
455 gdk_threads_leave ();
459 #define CONVERT(type, state) \
460 gdk_color_to_java_color (style->type[GTK_STATE_ ## state])
462 JNIEXPORT
void JNICALL
463 Java_gnu_java_awt_peer_gtk_GtkToolkit_loadSystemColors
464 (JNIEnv
*env
, jobject obj
__attribute__((unused
)),
470 colors
= (*env
)->GetIntArrayElements (env
, jcolors
, 0);
472 gdk_threads_enter ();
474 style
= gtk_widget_get_default_style ();
476 colors
[AWT_DESKTOP
] = CONVERT (bg
, SELECTED
);
477 colors
[AWT_ACTIVE_CAPTION
] = CONVERT (bg
, SELECTED
);
478 colors
[AWT_ACTIVE_CAPTION_TEXT
] = CONVERT (text
, SELECTED
);
479 colors
[AWT_ACTIVE_CAPTION_BORDER
] = CONVERT (fg
, NORMAL
);
480 colors
[AWT_INACTIVE_CAPTION
] = CONVERT (base
, INSENSITIVE
);
481 colors
[AWT_INACTIVE_CAPTION_TEXT
] = CONVERT (fg
, INSENSITIVE
);
482 colors
[AWT_INACTIVE_CAPTION_BORDER
] = CONVERT (fg
, INSENSITIVE
);
483 colors
[AWT_WINDOW
] = CONVERT (bg
, NORMAL
);
484 colors
[AWT_WINDOW_BORDER
] = CONVERT (fg
, NORMAL
);
485 colors
[AWT_WINDOW_TEXT
] = CONVERT (fg
, NORMAL
);
486 colors
[AWT_MENU
] = CONVERT (bg
, NORMAL
);
487 colors
[AWT_MENU_TEXT
] = CONVERT (fg
, NORMAL
);
488 colors
[AWT_TEXT
] = CONVERT (bg
, NORMAL
);
489 colors
[AWT_TEXT_TEXT
] = CONVERT (fg
, NORMAL
);
490 colors
[AWT_TEXT_HIGHLIGHT
] = CONVERT (bg
, SELECTED
);
491 colors
[AWT_TEXT_HIGHLIGHT_TEXT
] = CONVERT (fg
, SELECTED
);
492 colors
[AWT_TEXT_INACTIVE_TEXT
] = CONVERT (bg
, INSENSITIVE
);
493 colors
[AWT_CONTROL
] = CONVERT (bg
, NORMAL
);
494 colors
[AWT_CONTROL_TEXT
] = CONVERT (fg
, NORMAL
);
495 colors
[AWT_CONTROL_HIGHLIGHT
] = CONVERT (base
, ACTIVE
);
496 colors
[AWT_CONTROL_LT_HIGHLIGHT
] = CONVERT (bg
, PRELIGHT
);
497 colors
[AWT_CONTROL_SHADOW
] = CONVERT (bg
, ACTIVE
);
498 colors
[AWT_CONTROL_DK_SHADOW
] = CONVERT (fg
, INSENSITIVE
);
499 colors
[AWT_SCROLLBAR
] = CONVERT (base
, INSENSITIVE
);
500 colors
[AWT_INFO
] = CONVERT (bg
, NORMAL
);
501 colors
[AWT_INFO_TEXT
] = CONVERT (fg
, NORMAL
);
503 gdk_threads_leave ();
505 (*env
)->ReleaseIntArrayElements(env
, jcolors
, colors
, 0);
511 gdk_color_to_java_color (GdkColor gdk_color
)
518 factor
= 255.0 / 65535.0;
520 red
= (float) gdk_color
.red
* factor
;
521 green
= (float) gdk_color
.green
* factor
;
522 blue
= (float) gdk_color
.blue
* factor
;
524 return (jint
) (0xff000000 | (red
<< 16) | (green
<< 8) | blue
);