1 /* 2 * Copyright (c) 2016, 2020, 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 #ifdef HEADLESS 27 #error This file should not be included in headless library 28 #endif 29 30 #include <dlfcn.h> 31 #include "jvm_md.h" 32 #include <setjmp.h> 33 #include <string.h> 34 35 #include "jni_util.h" 36 #include "awt_Taskbar.h" 37 38 39 extern JavaVM *jvm; 40 41 #define NO_SYMBOL_EXCEPTION 1 42 43 #define UNITY_LIB_VERSIONED VERSIONED_JNI_LIB_NAME("unity", "9") 44 #define UNITY_LIB JNI_LIB_NAME("unity") 45 46 static jmp_buf j; 47 48 static void *unity_libhandle = NULL; 49 50 static DbusmenuMenuitem* menu = NULL; 51 UnityLauncherEntry* entry = NULL; 52 53 static jclass jTaskbarCls = NULL; 54 static jmethodID jTaskbarCallback = NULL; 55 static jmethodID jMenuItemGetLabel = NULL; 56 57 GList* globalRefs = NULL; 58 59 static void* dl_symbol(const char* name) { 60 void* result = dlsym(unity_libhandle, name); 61 if (!result) 62 longjmp(j, NO_SYMBOL_EXCEPTION); 63 64 return result; 65 } 66 67 static gboolean unity_load() { 68 unity_libhandle = dlopen(UNITY_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL); 69 if (unity_libhandle == NULL) { 70 unity_libhandle = dlopen(UNITY_LIB, RTLD_LAZY | RTLD_LOCAL); 71 if (unity_libhandle == NULL) { 72 return FALSE; 73 } 74 } 75 if (setjmp(j) == 0) { 76 fp_unity_launcher_entry_get_for_desktop_file = dl_symbol("unity_launcher_entry_get_for_desktop_file"); 77 fp_unity_launcher_entry_set_count = dl_symbol("unity_launcher_entry_set_count"); 78 fp_unity_launcher_entry_set_count_visible = dl_symbol("unity_launcher_entry_set_count_visible"); 79 fp_unity_launcher_entry_set_urgent = dl_symbol("unity_launcher_entry_set_urgent"); 80 fp_unity_launcher_entry_set_progress = dl_symbol("unity_launcher_entry_set_progress"); 81 fp_unity_launcher_entry_set_progress_visible = dl_symbol("unity_launcher_entry_set_progress_visible"); 82 83 fp_dbusmenu_menuitem_new = dl_symbol("dbusmenu_menuitem_new"); 84 fp_dbusmenu_menuitem_property_set = dl_symbol("dbusmenu_menuitem_property_set"); 85 fp_dbusmenu_menuitem_property_set_int = dl_symbol("dbusmenu_menuitem_property_set_int"); 86 fp_dbusmenu_menuitem_property_get_int = dl_symbol("dbusmenu_menuitem_property_get_int"); 87 fp_dbusmenu_menuitem_property_set = dl_symbol("dbusmenu_menuitem_property_set"); 88 fp_dbusmenu_menuitem_child_append = dl_symbol("dbusmenu_menuitem_child_append"); 89 fp_dbusmenu_menuitem_child_delete = dl_symbol("dbusmenu_menuitem_child_delete"); 90 fp_dbusmenu_menuitem_take_children = dl_symbol("dbusmenu_menuitem_take_children"); 91 fp_dbusmenu_menuitem_foreach = dl_symbol("dbusmenu_menuitem_foreach"); 92 fp_unity_launcher_entry_set_quicklist = dl_symbol("unity_launcher_entry_set_quicklist"); 93 fp_unity_launcher_entry_get_quicklist = dl_symbol("unity_launcher_entry_get_quicklist"); 94 } else { 95 dlclose(unity_libhandle); 96 unity_libhandle = NULL; 97 return FALSE; 98 } 99 return TRUE; 100 } 101 102 void callback(DbusmenuMenuitem* mi, guint ts, jobject data) { 103 JNIEnv* env = (JNIEnv*) JNU_GetEnv(jvm, JNI_VERSION_1_2); 104 (*env)->CallStaticVoidMethod(env, jTaskbarCls, jTaskbarCallback, data); 105 } 106 107 /* 108 * Class: sun_awt_X11_XTaskbarPeer 109 * Method: init 110 * Signature: (Ljava/lang/String;)Z 111 */ 112 JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XTaskbarPeer_init 113 (JNIEnv *env, jclass cls, jstring jname, jint version, jboolean verbose) { 114 jclass clazz; 115 116 jTaskbarCls = (*env)->NewGlobalRef(env, cls); 117 118 CHECK_NULL_RETURN(jTaskbarCallback = 119 (*env)->GetStaticMethodID(env, cls, "menuItemCallback", "(Ljava/awt/MenuItem;)V"), JNI_FALSE); 120 CHECK_NULL_RETURN( 121 clazz = (*env)->FindClass(env, "java/awt/MenuItem"), JNI_FALSE); 122 CHECK_NULL_RETURN( 123 jMenuItemGetLabel = (*env)->GetMethodID(env, clazz, "getLabel", "()Ljava/lang/String;"), JNI_FALSE); 124 125 if (gtk_load(env, version, verbose) && unity_load()) { 126 const gchar* name = (*env)->GetStringUTFChars(env, jname, NULL); 127 if (name) { 128 entry = fp_unity_launcher_entry_get_for_desktop_file(name); 129 (*env)->ReleaseStringUTFChars(env, jname, name); 130 return JNI_TRUE; 131 } 132 } 133 return JNI_FALSE; 134 } 135 136 /* 137 * Class: sun_awt_X11_XTaskbarPeer 138 * Method: runloop 139 * Signature: ()V 140 */ 141 JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_runloop 142 (JNIEnv *env, jclass cls) { 143 gtk->gdk_threads_enter(); 144 gtk->gtk_main(); 145 gtk->gdk_threads_leave(); 146 } 147 148 /* 149 * Class: sun_awt_X11_XTaskbarPeer 150 * Method: setBadge 151 * Signature: (JZ)V 152 */ 153 JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_setBadge 154 (JNIEnv *env, jobject obj, jlong value, jboolean visible) { 155 gtk->gdk_threads_enter(); 156 fp_unity_launcher_entry_set_count(entry, value); 157 fp_unity_launcher_entry_set_count_visible(entry, visible); 158 DbusmenuMenuitem* m; 159 if (m = fp_unity_launcher_entry_get_quicklist(entry)) { 160 fp_unity_launcher_entry_set_quicklist(entry, m); 161 } 162 gtk->gdk_threads_leave(); 163 } 164 165 /* 166 * Class: sun_awt_X11_XTaskbarPeer 167 * Method: setUrgent 168 * Signature: (Z)V 169 */ 170 JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_setUrgent 171 (JNIEnv *env, jobject obj, jboolean urgent) { 172 gtk->gdk_threads_enter(); 173 fp_unity_launcher_entry_set_urgent(entry, urgent); 174 DbusmenuMenuitem* m; 175 if (m = fp_unity_launcher_entry_get_quicklist(entry)) { 176 fp_unity_launcher_entry_set_quicklist(entry, m); 177 } 178 gtk->gdk_threads_leave(); 179 } 180 181 /* 182 * Class: sun_awt_X11_XTaskbarPeer 183 * Method: updateProgress 184 * Signature: (DZ)V 185 */ 186 JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_updateProgress 187 (JNIEnv *env, jobject obj, jdouble value, jboolean visible) { 188 gtk->gdk_threads_enter(); 189 fp_unity_launcher_entry_set_progress(entry, value); 190 fp_unity_launcher_entry_set_progress_visible(entry, visible); 191 DbusmenuMenuitem* m; 192 if (m = fp_unity_launcher_entry_get_quicklist(entry)) { 193 fp_unity_launcher_entry_set_quicklist(entry, m); 194 } 195 gtk->gdk_threads_leave(); 196 } 197 198 void deleteGlobalRef(gpointer data) { 199 JNIEnv* env = (JNIEnv*) JNU_GetEnv(jvm, JNI_VERSION_1_2); 200 (*env)->DeleteGlobalRef(env, data); 201 } 202 203 void fill_menu(JNIEnv *env, jobjectArray items) { 204 int index; 205 jsize length = (*env)->GetArrayLength(env, items); 206 for (index = 0; index < length; index++) { 207 jobject elem = (*env)->GetObjectArrayElement(env, items, index); 208 if ((*env)->ExceptionCheck(env)) { 209 break; 210 } 211 elem = (*env)->NewGlobalRef(env, elem); 212 213 globalRefs = gtk->g_list_append(globalRefs, elem); 214 215 jstring jlabel = (jstring) (*env)->CallObjectMethod(env, elem, jMenuItemGetLabel); 216 if (!(*env)->ExceptionCheck(env) && jlabel) { 217 const gchar* label = (*env)->GetStringUTFChars(env, jlabel, NULL); 218 if (label) { 219 DbusmenuMenuitem* mi = fp_dbusmenu_menuitem_new(); 220 if (!strcmp(label, "-")) { 221 fp_dbusmenu_menuitem_property_set(mi, "type", "separator"); 222 } else { 223 fp_dbusmenu_menuitem_property_set(mi, "label", label); 224 } 225 226 (*env)->ReleaseStringUTFChars(env, jlabel, label); 227 fp_dbusmenu_menuitem_child_append(menu, mi); 228 gtk->g_signal_connect_data(mi, "item_activated", 229 G_CALLBACK(callback), elem, NULL, 0); 230 } 231 } 232 } 233 } 234 235 /* 236 * Class: sun_awt_X11_XTaskbarPeer 237 * Method: setNativeMenu 238 * Signature: ([Ljava/awt/MenuItem;)V 239 */ 240 JNIEXPORT void JNICALL Java_sun_awt_X11_XTaskbarPeer_setNativeMenu 241 (JNIEnv *env, jobject obj, jobjectArray items) { 242 243 gtk->gdk_threads_enter(); 244 245 if (!menu) { 246 menu = fp_dbusmenu_menuitem_new(); 247 fp_unity_launcher_entry_set_quicklist(entry, menu); 248 } 249 250 GList* list = fp_dbusmenu_menuitem_take_children(menu); 251 gtk->g_list_free_full(list, gtk->g_object_unref); 252 253 gtk->g_list_free_full(globalRefs, deleteGlobalRef); 254 globalRefs = NULL; 255 256 if (items) { 257 fill_menu(env, items); 258 } 259 260 gtk->gdk_threads_leave(); 261 }