| /* |
| * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2023, JetBrains s.r.o.. All rights reserved. |
| * |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include "system_properties.h" |
| |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define UNKNOWN_RESULT -1 |
| #define SETTING_INTERFACE "org.freedesktop.portal.Settings" |
| #define SETTING_INTERFACE_METHOD "Read" |
| #define DESKTOP_DESTINATION "org.freedesktop.portal.Desktop" |
| #define DESKTOP_PATH "/org/freedesktop/portal/desktop" |
| #define REPLY_TIMEOUT 150 |
| |
| static DBusConnection *connection = NULL; |
| static JNIEnv *env = NULL; |
| static DBusApi *dBus = NULL; |
| static bool initialized = false; |
| static bool logEnabled = true; |
| extern JavaVM *jvm; |
| |
| static void printError(const char* fmt, ...) { |
| if (!logEnabled) { |
| return; |
| } |
| |
| env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| char* buf = (char*)malloc(1024); |
| |
| if (env && buf) { |
| va_list vargs; |
| va_start(vargs, fmt); |
| vsnprintf(buf, 1024, fmt, vargs); |
| jstring text = JNU_NewStringPlatform(env, buf); |
| free(buf); |
| va_end(vargs); |
| |
| jboolean ignoreException; |
| JNU_CallStaticMethodByName(env, &ignoreException, "sun/awt/UNIXToolkit", "printError", |
| "(Ljava/lang/String;)V", text); |
| } |
| } |
| |
| static bool dbusCheckError(DBusError *err, const char *msg) { |
| bool is_error_set = dBus->dbus_error_is_set(err); |
| if (is_error_set) { |
| printError("DBus error: %s. %s\n", msg, err->message); |
| dBus->dbus_error_free(err); |
| } |
| return is_error_set; |
| } |
| |
| bool SystemProperties_setup(DBusApi *dBus_, JNIEnv *env_) { |
| env = env_; |
| dBus = dBus_; |
| DBusError err; |
| int ret; |
| |
| dBus->dbus_error_init(&err); |
| if ((connection = dBus->dbus_bus_get(DBUS_BUS_SESSION, &err)) == NULL) { |
| printError("DBus error: connection is Null\n"); |
| return false; |
| } |
| if (dbusCheckError(&err, "connection error")) { |
| return false; |
| } |
| |
| ret = dBus->dbus_bus_request_name(connection, "dbus.JBR.server", DBUS_NAME_FLAG_REPLACE_EXISTING , &err); |
| if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER && ret != DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { |
| printError("DBus error: Failed to acquire service name \n"); |
| return false; |
| } |
| if (dbusCheckError(&err, "error request 'dbus.JBR.server' name on the bus")) { |
| return false; |
| } |
| |
| dBus->dbus_connection_flush(connection); |
| initialized = true; |
| |
| return true; |
| } |
| |
| // current implementation of object decomposition supports only |
| // primitive types (including a recursive type wrapper) |
| static bool getBasicIter(void *val, DBusMessageIter *iter, int demand_type) { |
| int type = dBus->dbus_message_iter_get_arg_type(iter); |
| switch (type) |
| { |
| case DBUS_TYPE_INT16: |
| case DBUS_TYPE_UINT16: |
| case DBUS_TYPE_INT32: |
| case DBUS_TYPE_UINT32: |
| case DBUS_TYPE_INT64: |
| case DBUS_TYPE_UINT64: |
| case DBUS_TYPE_DOUBLE: |
| case DBUS_TYPE_BYTE: |
| case DBUS_TYPE_BOOLEAN: |
| case DBUS_TYPE_STRING: |
| { |
| if (type != demand_type) { |
| return false; |
| } |
| dBus->dbus_message_iter_get_basic(iter, val); |
| return true; |
| } |
| case DBUS_TYPE_VARIANT: |
| { |
| DBusMessageIter sub_iter; |
| dBus->dbus_message_iter_recurse(iter, &sub_iter); |
| bool res = getBasicIter(val, &sub_iter, demand_type); |
| // current implementation doesn't support types with multiple fields |
| if (dBus->dbus_message_iter_next(iter)) { |
| return false; |
| } |
| return res; |
| } |
| case DBUS_TYPE_INVALID: |
| default: |
| return false; |
| } |
| } |
| |
| static bool sendDBusMessageWithReply(const char *messages[], int message_count, void *val, int demand_type) { |
| DBusError error; |
| DBusMessage *message = NULL; |
| DBusMessage *reply = NULL; |
| DBusMessageIter iter; |
| bool res = false; |
| |
| if (!initialized) { |
| return false; |
| } |
| |
| dBus->dbus_error_init(&error); |
| message = dBus->dbus_message_new_method_call(NULL, DESKTOP_PATH, SETTING_INTERFACE, SETTING_INTERFACE_METHOD); |
| if (message == NULL) { |
| printError("DBus error: cannot allocate message\n"); |
| goto cleanup; |
| } |
| |
| dBus->dbus_message_set_auto_start(message, true); |
| if (!dBus->dbus_message_set_destination(message, DESKTOP_DESTINATION)) { |
| printError("DBus error: cannot set destination\n"); |
| goto cleanup; |
| } |
| |
| dBus->dbus_message_iter_init_append(message, &iter); |
| |
| for (int i = 0; i < message_count; i++) { |
| if (!dBus->dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &messages[i])) { |
| printError("DBus error: cannot append to message\n"); |
| goto cleanup; |
| } |
| } |
| |
| if ((reply = dBus->dbus_connection_send_with_reply_and_block(connection, message, REPLY_TIMEOUT, &error)) == NULL) { |
| printError("DBus error: cannot get reply or sent message. %s\n", dBus->dbus_error_is_set(&error) ? error.message : ""); |
| goto cleanup; |
| } |
| |
| if (!dBus->dbus_message_iter_init (reply, &iter)) { |
| printError("DBus error: cannot process message\n"); |
| goto cleanup; |
| } |
| |
| res = getBasicIter(val, &iter, demand_type); |
| |
| cleanup: |
| if (reply) { |
| dBus->dbus_message_unref(reply); |
| } |
| if (message) { |
| dBus->dbus_message_unref(message); |
| } |
| return res; |
| } |
| |
| JNIEXPORT jint JNICALL Java_sun_awt_UNIXToolkit_isSystemDarkColorScheme() { |
| static int use_freedesktop_appearance = -1; |
| const char *freedesktop_appearance_messages[] = {"org.freedesktop.appearance", "color-scheme"}; |
| const char *gnome_desktop_messages[] = {"org.gnome.desktop.interface", "gtk-theme"}; |
| |
| if (use_freedesktop_appearance == -1) { |
| unsigned int res = 0; |
| logEnabled = false; |
| use_freedesktop_appearance = |
| sendDBusMessageWithReply(freedesktop_appearance_messages, 2, &res, DBUS_TYPE_UINT32); |
| logEnabled = true; |
| } |
| |
| if (use_freedesktop_appearance) { |
| unsigned int res = 0; |
| if (!sendDBusMessageWithReply(freedesktop_appearance_messages, 2, &res, DBUS_TYPE_UINT32)) { |
| return UNKNOWN_RESULT; |
| } |
| return res; |
| } else { |
| char *res = NULL; |
| if (!sendDBusMessageWithReply(gnome_desktop_messages, 2, &res, DBUS_TYPE_STRING)) { |
| return UNKNOWN_RESULT; |
| } |
| return (res != NULL) ? strstr(res, "dark") != NULL : UNKNOWN_RESULT; |
| } |
| } |
| |
| JNIEXPORT void JNICALL Java_sun_awt_UNIXToolkit_toolkitInit() { |
| DBusApi *dBus = DBusApi_setupDBusDefault(); |
| if (dBus) { |
| SystemProperties_setup(dBus, env); |
| } |
| } |