1 /*
   2  * Copyright (c) 2013, 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 <stdlib.h>
  27 
  28 // The includes below will end up including gio.h without an extern "C",
  29 // so we include gio.h here early on to make sure that the names are mangled
  30 // using the C conventions rather than the C++ conventions.
  31 extern "C" {
  32 #include <gio/gio.h>
  33 }
  34 
  35 #include "glass_screen.h"
  36 #include "glass_general.h"
  37 
  38 #include <X11/Xatom.h>
  39 #include <gdk/gdk.h>
  40 #include <gdk/gdkx.h>
  41 
  42 jfloat OverrideUIScale = -1.0f;
  43 
  44 static guint get_current_desktop(GdkScreen *screen) {
  45     Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
  46     Atom currentDesktopAtom = XInternAtom(display, "_NET_CURRENT_DESKTOP", True);
  47     guint ret = 0;
  48 
  49     Atom type;
  50     int format;
  51     gulong num, left;
  52     unsigned long *data = NULL;
  53 
  54     if (currentDesktopAtom == None) {
  55         return 0;
  56     }
  57 
  58     int result = XGetWindowProperty(display,
  59                                     GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
  60                                     currentDesktopAtom, 0, G_MAXLONG, False, XA_CARDINAL,
  61                                     &type, &format, &num, &left, (unsigned char **)&data);
  62 
  63     if ((result == Success) && (data != NULL)) {
  64         if (type == XA_CARDINAL && format == 32) {
  65             ret = data[0];
  66         }
  67 
  68         XFree(data);
  69     }
  70 
  71     return ret;
  72 
  73 }
  74 
  75 static GdkRectangle get_screen_workarea(GdkScreen *screen) {
  76     Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
  77     GdkRectangle ret = { 0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen)};
  78 
  79     Atom workareaAtom = XInternAtom(display, "_NET_WORKAREA", True);
  80 
  81     Atom type;
  82     int format;
  83     gulong num, left;
  84     unsigned long *data = NULL;
  85 
  86     if (workareaAtom == None) {
  87         return ret;
  88     }
  89 
  90     int result = XGetWindowProperty(display,
  91                                     GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
  92                                     workareaAtom, 0, G_MAXLONG, False, AnyPropertyType,
  93                                     &type, &format, &num, &left, (unsigned char **)&data);
  94 
  95     if ((result == Success) && (data != NULL)) {
  96         if (type != None && format == 32) {
  97             guint current_desktop = get_current_desktop(screen);
  98             if (current_desktop < num / 4) {
  99                 ret.x = data[current_desktop * 4];
 100                 ret.y = data[current_desktop * 4 + 1];
 101                 ret.width = data[current_desktop * 4 + 2];
 102                 ret.height = data[current_desktop * 4 + 3];
 103             }
 104         }
 105 
 106         XFree(data);
 107     }
 108 
 109     return ret;
 110 
 111 }
 112 
 113 #undef DEBUG_GSETTINGS
 114 
 115 static guint gsettings_get_guint(const gchar *schema_name,
 116                                  const gchar *key_name,
 117                                  int defval)
 118 {
 119     GSettingsSchemaSource *default_schema_source =
 120             g_settings_schema_source_get_default();
 121     if (default_schema_source == NULL) {
 122 #ifdef DEBUG_GSETTINGS
 123         fprintf(stderr, "No schema source dir found!\n");
 124 #endif
 125         return defval;
 126     }
 127     GSettingsSchema *the_schema =
 128             g_settings_schema_source_lookup(default_schema_source, schema_name, TRUE);
 129     if (the_schema == NULL) {
 130 #ifdef DEBUG_GSETTINGS
 131         fprintf(stderr, "schema '%s' not found!\n", schema_name);
 132 #endif
 133         return defval;
 134     }
 135 #ifdef DEBUG_GSETTINGS
 136     fprintf(stderr, "found schema '%s'\n", schema_name);
 137 #endif
 138     GSettings *gset = g_settings_new(schema_name);
 139     guint val = g_settings_get_uint(gset, key_name);
 140 #ifdef DEBUG_GSETTINGS
 141     fprintf(stderr, "...and key '%s'\n", key_name);
 142 #endif
 143     return val;
 144 }
 145 
 146 static jobject createJavaScreen(JNIEnv* env, GdkScreen* screen, gint monitor_idx)
 147 {
 148     GdkRectangle workArea = get_screen_workarea(screen);
 149     LOG4("Work Area: x:%d, y:%d, w:%d, h:%d\n", workArea.x, workArea.y, workArea.width, workArea.height);
 150 
 151     GdkRectangle monitor_geometry;
 152     gdk_screen_get_monitor_geometry(screen, monitor_idx, &monitor_geometry);
 153     LOG1("convert monitor[%d] -> glass Screen\n", monitor_idx)
 154     LOG4("[x: %d y: %d w: %d h: %d]\n",
 155          monitor_geometry.x, monitor_geometry.y,
 156          monitor_geometry.width, monitor_geometry.height)
 157 
 158     GdkVisual* visual = gdk_screen_get_system_visual(screen);
 159 
 160     GdkRectangle working_monitor_geometry;
 161     gdk_rectangle_intersect(&workArea, &monitor_geometry, &working_monitor_geometry);
 162 
 163     jfloat uiScale;
 164     if (OverrideUIScale > 0.0f) {
 165         uiScale = OverrideUIScale;
 166     } else {
 167         char *scale_str = getenv("GDK_SCALE");
 168         int gdk_scale = (scale_str == NULL) ? -1 : atoi(scale_str);
 169         if (gdk_scale > 0) {
 170             uiScale = (jfloat) gdk_scale;
 171         } else {
 172             uiScale = (jfloat) gsettings_get_guint("org.gnome.desktop.interface",
 173                                                    "scaling-factor", 0);
 174             if (uiScale < 1) {
 175                 uiScale = 1;
 176             }
 177         }
 178     }
 179 
 180     jint mx = monitor_geometry.x / uiScale;
 181     jint my = monitor_geometry.y / uiScale;
 182     jint mw = monitor_geometry.width / uiScale;
 183     jint mh = monitor_geometry.height / uiScale;
 184     jint wx = working_monitor_geometry.x / uiScale;
 185     jint wy = working_monitor_geometry.y / uiScale;
 186     jint ww = working_monitor_geometry.width / uiScale;
 187     jint wh = working_monitor_geometry.height / uiScale;
 188 
 189     gint mmW = gdk_screen_get_monitor_width_mm(screen, monitor_idx);
 190     gint mmH = gdk_screen_get_monitor_height_mm(screen, monitor_idx);
 191     if (mmW <= 0 || mmH <= 0) {
 192         if (gdk_screen_get_n_monitors(screen) == 1) {
 193             mmW = gdk_screen_get_width_mm(screen);
 194             mmH = gdk_screen_get_height_mm(screen);
 195         }
 196     }
 197     jint dpiX, dpiY;
 198     if (mmW <= 0 || mmH <= 0) {
 199         dpiX = dpiY = 96;
 200     } else {
 201         dpiX = (mw * 254) / (mmW * 10);
 202         dpiY = (mh * 254) / (mmH * 10);
 203     }
 204 
 205     jobject jScreen = env->NewObject(jScreenCls, jScreenInit,
 206                                      (jlong)monitor_idx,
 207 
 208                                      (visual ? glass_gdk_visual_get_depth(visual) : 0),
 209 
 210                                      mx, my, mw, mh,
 211 
 212                                      monitor_geometry.x,
 213                                      monitor_geometry.y,
 214                                      monitor_geometry.width,
 215                                      monitor_geometry.height,
 216 
 217                                      wx, wy, ww, wh,
 218 
 219                                      dpiX, dpiY,
 220                                      uiScale, uiScale, uiScale, uiScale);
 221 
 222     JNI_EXCEPTION_TO_CPP(env);
 223     return jScreen;
 224 }
 225 
 226 jobject createJavaScreen(JNIEnv* env, gint monitor_idx) {
 227     GdkScreen *default_gdk_screen = gdk_screen_get_default();
 228     try {
 229         return createJavaScreen(env, default_gdk_screen, monitor_idx);
 230     } catch (jni_exception&) {
 231         return NULL;
 232     }
 233 }
 234 
 235 jobjectArray rebuild_screens(JNIEnv* env) {
 236     GdkScreen *default_gdk_screen = gdk_screen_get_default();
 237     gint n_monitors = gdk_screen_get_n_monitors(default_gdk_screen);
 238 
 239     jobjectArray jscreens = env->NewObjectArray(n_monitors, jScreenCls, NULL);
 240     JNI_EXCEPTION_TO_CPP(env)
 241     LOG1("Available monitors: %d\n", n_monitors)
 242 
 243     int i;
 244     for (i=0; i < n_monitors; i++) {
 245         env->SetObjectArrayElement(jscreens, i, createJavaScreen(env, default_gdk_screen, i));
 246         JNI_EXCEPTION_TO_CPP(env)
 247     }
 248 
 249     return jscreens;
 250 }
 251 
 252 
 253 glong getScreenPtrForLocation(gint x, gint y) {
 254     //Note: we are relying on the fact that javafx_screen_id == gdk_monitor_id
 255     return gdk_screen_get_monitor_at_point(gdk_screen_get_default(), x, y);
 256 }
 257 
 258 void screen_settings_changed(GdkScreen* screen, gpointer user_data) {
 259     (void)screen;
 260     (void)user_data;
 261 
 262     mainEnv->CallStaticVoidMethod(jScreenCls, jScreenNotifySettingsChanged);
 263     LOG_EXCEPTION(mainEnv);
 264 }