1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include <dlfcn.h>
27#include "jvm_md.h"
28#include <setjmp.h>
29#include <string.h>
30
31#include "jni_util.h"
32#include "awt_Taskbar.h"
33
34
35extern JavaVM *jvm;
36
37#define NO_SYMBOL_EXCEPTION 1
38
39#define UNITY_LIB_VERSIONED VERSIONED_JNI_LIB_NAME("unity", "9")
40#define UNITY_LIB JNI_LIB_NAME("unity")
41
42static jmp_buf j;
43
44static void *unity_libhandle = NULL;
45
46static DbusmenuMenuitem* menu = NULL;
47UnityLauncherEntry* entry = NULL;
48
49static jclass jTaskbarCls = NULL;
50static jmethodID jTaskbarCallback = NULL;
51static jmethodID jMenuItemGetLabel = NULL;
52
53GList* globalRefs = NULL;
54
55static void* dl_symbol(const char* name) {
56 void* result = dlsym(unity_libhandle, name);
57 if (!result)
58 longjmp(j, NO_SYMBOL_EXCEPTION);
59
60 return result;
61}
62
63static gboolean unity_load() {
64 unity_libhandle = dlopen(UNITY_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL);
65 if (unity_libhandle == NULL) {
66 unity_libhandle = dlopen(UNITY_LIB, RTLD_LAZY | RTLD_LOCAL);
67 if (unity_libhandle == NULL) {
68 return FALSE;
69 }
70 }
71 if (setjmp(j) == 0) {
72 fp_unity_launcher_entry_get_for_desktop_file = dl_symbol("unity_launcher_entry_get_for_desktop_file");
73 fp_unity_launcher_entry_set_count = dl_symbol("unity_launcher_entry_set_count");
74 fp_unity_launcher_entry_set_count_visible = dl_symbol("unity_launcher_entry_set_count_visible");
75 fp_unity_launcher_entry_set_urgent = dl_symbol("unity_launcher_entry_set_urgent");
76 fp_unity_launcher_entry_set_progress = dl_symbol("unity_launcher_entry_set_progress");
77 fp_unity_launcher_entry_set_progress_visible = dl_symbol("unity_launcher_entry_set_progress_visible");
78
79 fp_dbusmenu_menuitem_new = dl_symbol("dbusmenu_menuitem_new");
80 fp_dbusmenu_menuitem_property_set = dl_symbol("dbusmenu_menuitem_property_set");
81 fp_dbusmenu_menuitem_property_set_int = dl_symbol("dbusmenu_menuitem_property_set_int");
82 fp_dbusmenu_menuitem_property_get_int = dl_symbol("dbusmenu_menuitem_property_get_int");
83 fp_dbusmenu_menuitem_property_set = dl_symbol("dbusmenu_menuitem_property_set");
84 fp_dbusmenu_menuitem_child_append = dl_symbol("dbusmenu_menuitem_child_append");
85 fp_dbusmenu_menuitem_child_delete = dl_symbol("dbusmenu_menuitem_child_delete");
86 fp_dbusmenu_menuitem_take_children = dl_symbol("dbusmenu_menuitem_take_children");
87 fp_dbusmenu_menuitem_foreach = dl_symbol("dbusmenu_menuitem_foreach");
88 fp_unity_launcher_entry_set_quicklist = dl_symbol("unity_launcher_entry_set_quicklist");
89 fp_unity_launcher_entry_get_quicklist = dl_symbol("unity_launcher_entry_get_quicklist");
90 } else {
91 dlclose(unity_libhandle);
92 unity_libhandle = NULL;
93 return FALSE;
94 }
95 return TRUE;
96}
97
98void callback(DbusmenuMenuitem* mi, guint ts, jobject data) {
99 JNIEnv* env = (JNIEnv*) JNU_GetEnv(jvm, JNI_VERSION_1_2);
100 (*env)->CallStaticVoidMethod(env, jTaskbarCls, jTaskbarCallback, data);
101}
102
103/*
104 * Class: sun_awt_X11_XTaskbarPeer
105 * Method: init
106 * Signature: (Ljava/lang/String;)Z
107 */
108JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XTaskbarPeer_init
109(JNIEnv *env, jclass cls, jstring jname, jint version, jboolean verbose) {
110 jclass clazz;
111
112 jTaskbarCls = (*env)->NewGlobalRef(env, cls);
113
114 CHECK_NULL_RETURN(jTaskbarCallback =
115 (*env)->GetStaticMethodID(env, cls, "menuItemCallback", "(Ljava/awt/MenuItem;)V"), JNI_FALSE);
116 CHECK_NULL_RETURN(
117 clazz = (*env)->FindClass(env, "java/awt/MenuItem"), JNI_FALSE);
118 CHECK_NULL_RETURN(
119 jMenuItemGetLabel = (*env)->GetMethodID(env, clazz, "getLabel", "()Ljava/lang/String;"), JNI_FALSE);
120
121 if (gtk_load(env, version, verbose) && unity_load()) {
122 const gchar* name = (*env)->GetStringUTFChars(env, jname, NULL);
123 if (name) {
124 entry = fp_unity_launcher_entry_get_for_desktop_file(name);
125 (*env)->ReleaseStringUTFChars(env, jname, name);
126 return JNI_TRUE;
127 }
128 }
129 return JNI_FALSE;
130}
131
132/*
133 * Class: sun_awt_X11_XTaskbarPeer
134 * Method: runloop
135 * Signature: ()V
136 */
137JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_runloop
138(JNIEnv *env, jclass cls) {
139 gtk->gdk_threads_enter();
140 gtk->gtk_main();
141 gtk->gdk_threads_leave();
142}
143
144/*
145 * Class: sun_awt_X11_XTaskbarPeer
146 * Method: setBadge
147 * Signature: (JZ)V
148 */
149JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_setBadge
150(JNIEnv *env, jobject obj, jlong value, jboolean visible) {
151 gtk->gdk_threads_enter();
152 fp_unity_launcher_entry_set_count(entry, value);
153 fp_unity_launcher_entry_set_count_visible(entry, visible);
154 DbusmenuMenuitem* m;
155 if (m = fp_unity_launcher_entry_get_quicklist(entry)) {
156 fp_unity_launcher_entry_set_quicklist(entry, m);
157 }
158 gtk->gdk_threads_leave();
159}
160
161/*
162 * Class: sun_awt_X11_XTaskbarPeer
163 * Method: setUrgent
164 * Signature: (Z)V
165 */
166JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_setUrgent
167(JNIEnv *env, jobject obj, jboolean urgent) {
168 gtk->gdk_threads_enter();
169 fp_unity_launcher_entry_set_urgent(entry, urgent);
170 DbusmenuMenuitem* m;
171 if (m = fp_unity_launcher_entry_get_quicklist(entry)) {
172 fp_unity_launcher_entry_set_quicklist(entry, m);
173 }
174 gtk->gdk_threads_leave();
175}
176
177/*
178 * Class: sun_awt_X11_XTaskbarPeer
179 * Method: updateProgress
180 * Signature: (DZ)V
181 */
182JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_updateProgress
183(JNIEnv *env, jobject obj, jdouble value, jboolean visible) {
184 gtk->gdk_threads_enter();
185 fp_unity_launcher_entry_set_progress(entry, value);
186 fp_unity_launcher_entry_set_progress_visible(entry, visible);
187 DbusmenuMenuitem* m;
188 if (m = fp_unity_launcher_entry_get_quicklist(entry)) {
189 fp_unity_launcher_entry_set_quicklist(entry, m);
190 }
191 gtk->gdk_threads_leave();
192}
193
194void deleteGlobalRef(gpointer data) {
195 JNIEnv* env = (JNIEnv*) JNU_GetEnv(jvm, JNI_VERSION_1_2);
196 (*env)->DeleteGlobalRef(env, data);
197}
198
199void fill_menu(JNIEnv *env, jobjectArray items) {
200 int index;
201 jsize length = (*env)->GetArrayLength(env, items);
202 for (index = 0; index < length; index++) {
203 jobject elem = (*env)->GetObjectArrayElement(env, items, index);
204 if ((*env)->ExceptionCheck(env)) {
205 break;
206 }
207 elem = (*env)->NewGlobalRef(env, elem);
208
209 globalRefs = gtk->g_list_append(globalRefs, elem);
210
211 jstring jlabel = (jstring) (*env)->CallObjectMethod(env, elem, jMenuItemGetLabel);
212 if (!(*env)->ExceptionCheck(env) && jlabel) {
213 const gchar* label = (*env)->GetStringUTFChars(env, jlabel, NULL);
214 if (label) {
215 DbusmenuMenuitem* mi = fp_dbusmenu_menuitem_new();
216 if (!strcmp(label, "-")) {
217 fp_dbusmenu_menuitem_property_set(mi, "type", "separator");
218 } else {
219 fp_dbusmenu_menuitem_property_set(mi, "label", label);
220 }
221
222 (*env)->ReleaseStringUTFChars(env, jlabel, label);
223 fp_dbusmenu_menuitem_child_append(menu, mi);
224 gtk->g_signal_connect_data(mi, "item_activated",
225 G_CALLBACK(callback), elem, NULL, 0);
226 }
227 }
228 }
229}
230
231/*
232 * Class: sun_awt_X11_XTaskbarPeer
233 * Method: setNativeMenu
234 * Signature: ([Ljava/awt/MenuItem;)V
235 */
236JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_setNativeMenu
237(JNIEnv *env, jobject obj, jobjectArray items) {
238
239 gtk->gdk_threads_enter();
240
241 if (!menu) {
242 menu = fp_dbusmenu_menuitem_new();
243 fp_unity_launcher_entry_set_quicklist(entry, menu);
244 }
245
246 GList* list = fp_dbusmenu_menuitem_take_children(menu);
247 gtk->g_list_free_full(list, gtk->g_object_unref);
248
249 gtk->g_list_free_full(globalRefs, deleteGlobalRef);
250 globalRefs = NULL;
251
252 if (items) {
253 fill_menu(env, items);
254 }
255
256 gtk->gdk_threads_leave();
257}
258