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