diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c8a29e75..b7cb8f8b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -50,9 +50,10 @@ Dependencies
* autotools, gettext
* intltool, libtool
+* libX11
* libdrm (Optional, for DRM support)
* libxcb, libxcb-randr (Optional, for RandR support)
-* libX11, libXxf86vm (Optional, for VidMode support)
+* libXxf86vm (Optional, for VidMode support)
* Glib 2 (Optional, for GeoClue2 support)
* python3, pygobject, pyxdg (Optional, for GUI support)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5ef8dacc..985990d0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,7 @@ data/applications/redshift-gtk.desktop.in
src/redshift.c
src/options.c
src/config-ini.c
+src/fullscreen.c
src/gamma-drm.c
src/gamma-randr.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 8aa96ead..9f58d5ff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,7 +19,8 @@ redshift_SOURCES = \
redshift.c redshift.h \
signals.c signals.h \
solar.c solar.h \
- systemtime.c systemtime.h
+ systemtime.c systemtime.h \
+ fullscreen.c fullscreen.h
EXTRA_redshift_SOURCES = \
gamma-drm.c gamma-drm.h \
diff --git a/src/fullscreen.c b/src/fullscreen.c
new file mode 100644
index 00000000..2c0ba018
--- /dev/null
+++ b/src/fullscreen.c
@@ -0,0 +1,93 @@
+/* fullscreen.h -- Fullscreen detector
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift 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 for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2021 Angelo Elias Dalzotto
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+#include
+#include
+
+#ifdef ENABLE_NLS
+# include
+# define _(s) gettext(s)
+#else
+# define _(s) s
+#endif
+
+#ifndef _WIN32
+# include
+#endif
+
+#include "fullscreen.h"
+#include "redshift.h"
+
+#ifndef _WIN32
+static Display *display;
+#endif
+
+static int
+fullscreen_init()
+{
+#ifndef _WIN32
+ display = XOpenDisplay(NULL);
+ if (display == NULL) {
+ fprintf(stderr, _("X request failed: %s\n"), "XOpenDisplay");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+static int
+fullscreen_check()
+{
+#ifndef _WIN32
+ Window window;
+ int revert_to = RevertToParent;
+ int result = XGetInputFocus(display, &window, &revert_to);
+
+ int win_x, win_y, win_w, win_h, win_b, win_d;
+ int scr_x, scr_y, scr_w, scr_h, scr_b, scr_d;
+ if (result) {
+ Window rootWindow;
+ result = XGetGeometry(display, window, &rootWindow, &win_x, &win_y, &win_w, &win_h, &win_b, &win_d);
+ if (rootWindow) {
+ result = XGetGeometry(display, rootWindow, &rootWindow, &scr_x, &scr_y, &scr_w, &scr_h, &scr_b, &scr_d);
+ }
+ }
+
+ if (result && win_w == scr_w && win_h == scr_h) {
+ return 1;
+ } else {
+#endif
+ return 0;
+#ifndef _WIN32
+ }
+#endif
+}
+
+const fullscreen_t fullscreen = {
+ "fullscreen",
+ (fullscreen_init_func *)fullscreen_init,
+ (fullscreen_check_func *)fullscreen_check,
+};
diff --git a/src/fullscreen.h b/src/fullscreen.h
new file mode 100644
index 00000000..f12683af
--- /dev/null
+++ b/src/fullscreen.h
@@ -0,0 +1,28 @@
+/* fullscreen.h -- Fullscreen detector header
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift 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 for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2021 Angelo Elias Dalzotto
+*/
+
+#ifndef REDSHIFT_FULLSCREEN_H
+#define REDSHIFT_FULLSCREEN_H
+
+#include "redshift.h"
+
+extern const fullscreen_t fullscreen;
+
+#endif /* ! REDSHIFT_FULLSCREEN_H */
+
diff --git a/src/options.c b/src/options.c
index 33bf623a..58e0d1b0 100644
--- a/src/options.c
+++ b/src/options.c
@@ -178,6 +178,7 @@ print_help(const char *program_name)
no-wrap */
fputs(_(" -b DAY:NIGHT\tScreen brightness to apply (between 0.1 and 1.0)\n"
" -c FILE\tLoad settings from specified configuration file\n"
+ " -f BOOL\tEnable or disable fullscreen windows bypass\n"
" -g R:G:B\tAdditional gamma correction to apply\n"
" -l LAT:LON\tYour current location\n"
" -l PROVIDER\tSelect provider for automatic"
@@ -323,6 +324,7 @@ options_init(options_t *options)
options->preserve_gamma = 1;
options->mode = PROGRAM_MODE_CONTINUAL;
options->verbose = 0;
+ options->fullscreen_check = 1;
}
/* Parse a single option from the command-line. */
@@ -345,6 +347,9 @@ parse_command_line_option(
free(options->config_filepath);
options->config_filepath = strdup(value);
break;
+ case 'f':
+ options->fullscreen_check = atoi(value);
+ break;
case 'g':
r = parse_gamma_string(value, options->scheme.day.gamma);
if (r < 0) {
@@ -495,7 +500,7 @@ options_parse_args(
{
const char* program_name = argv[0];
int opt;
- while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:pPrt:vVx")) != -1) {
+ while ((opt = getopt(argc, argv, "b:c:f:g:hl:m:oO:pPrt:vVx")) != -1) {
char option = opt;
int r = parse_command_line_option(
option, optarg, options, program_name, gamma_methods,
diff --git a/src/options.h b/src/options.h
index 9993a07f..2e331db5 100644
--- a/src/options.h
+++ b/src/options.h
@@ -29,6 +29,7 @@ typedef struct {
transition_scheme_t scheme;
program_mode_t mode;
int verbose;
+ int fullscreen_check;
/* Temperature to set in manual mode. */
int temp_set;
diff --git a/src/redshift-gtk/controller.py b/src/redshift-gtk/controller.py
index 24c58ae7..b3704caf 100644
--- a/src/redshift-gtk/controller.py
+++ b/src/redshift-gtk/controller.py
@@ -34,6 +34,7 @@ class RedshiftController(GObject.GObject):
__gsignals__ = {
'inhibit-changed': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
+ 'fs_bypass_inhibit-changed': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
'temperature-changed': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
'period-changed': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
'location-changed': (GObject.SIGNAL_RUN_FIRST, None, (float, float)),
@@ -51,6 +52,7 @@ def __init__(self, args):
# Initialize state variables
self._inhibited = False
+ self._fs_bypass_inhibited = False
self._temperature = 0
self._period = 'Unknown'
self._location = (0.0, 0.0)
@@ -112,6 +114,11 @@ def relay_signal_handler(signal):
def inhibited(self):
"""Current inhibition state."""
return self._inhibited
+
+ @property
+ def fs_bypass_inhibited(self):
+ """Current fullscreen bypass inhibition state."""
+ return self._fs_bypass_inhibited
@property
def temperature(self):
@@ -133,6 +140,11 @@ def set_inhibit(self, inhibit):
if inhibit != self._inhibited:
self._child_toggle_inhibit()
+ def set_fs_bypass_inhibit(self, fs_bypass_inhibit):
+ """Set inhibition state."""
+ if fs_bypass_inhibit != self._fs_bypass_inhibited:
+ self._child_toggle_fs_bypass_inhibit()
+
def _child_signal(self, sg):
"""Send signal to child process."""
os.kill(self._process[0], sg)
@@ -141,6 +153,10 @@ def _child_toggle_inhibit(self):
"""Sends a request to the child process to toggle state."""
self._child_signal(signal.SIGUSR1)
+ def _child_toggle_fs_bypass_inhibit(self):
+ """Sends a request to the child process to toggle fullscreen bypass state."""
+ self._child_signal(signal.SIGUSR2)
+
def _child_cb(self, pid, status, data=None):
"""Called when the child process exists."""
@@ -175,6 +191,11 @@ def parse_coord(s):
if new_inhibited != self._inhibited:
self._inhibited = new_inhibited
self.emit('inhibit-changed', new_inhibited)
+ elif key == 'Fullscreen bypass':
+ new_fs_bypass_inhibited = value != 'Enabled'
+ if new_fs_bypass_inhibited != self._fs_bypass_inhibited:
+ self._fs_bypass_inhibited = new_fs_bypass_inhibited
+ self.emit('fs_bypass_inhibit-changed', new_fs_bypass_inhibited)
elif key == 'Color temperature':
new_temperature = int(value.rstrip('K'), 10)
if new_temperature != self._temperature:
diff --git a/src/redshift-gtk/statusicon.py b/src/redshift-gtk/statusicon.py
index b4adfb00..f6705fa0 100644
--- a/src/redshift-gtk/statusicon.py
+++ b/src/redshift-gtk/statusicon.py
@@ -93,6 +93,13 @@ def __init__(self, controller):
suspend_menu_item.set_submenu(suspend_menu)
self.status_menu.append(suspend_menu_item)
+ # Add fullscreen bypass toggle
+ self.fs_bypass_toggle_item = Gtk.CheckMenuItem.new_with_label(
+ _('Fullscreen bypass'))
+ self.fs_bypass_toggle_item.connect(
+ 'activate', self.fs_bypass_toggle_item_cb)
+ self.status_menu.append(self.fs_bypass_toggle_item)
+
# Add autostart option
if utils.supports_autostart():
autostart_item = Gtk.CheckMenuItem.new_with_label(_('Autostart'))
@@ -157,6 +164,8 @@ def __init__(self, controller):
# Setup signals to property changes
self._controller.connect('inhibit-changed', self.inhibit_change_cb)
+ self._controller.connect(
+ 'fs_bypass_inhibit-changed', self.fs_bypass_inhibit_change_cb)
self._controller.connect('period-changed', self.period_change_cb)
self._controller.connect(
'temperature-changed', self.temperature_change_cb)
@@ -167,6 +176,7 @@ def __init__(self, controller):
# Set info box text
self.change_inhibited(self._controller.inhibited)
+ self.change_fs_bypass_inhibited(self._controller.fs_bypass_inhibited)
self.change_period(self._controller.period)
self.change_temperature(self._controller.temperature)
self.change_location(self._controller.location)
@@ -235,6 +245,17 @@ def toggle_item_cb(self, widget, data=None):
self.remove_suspend_timer()
self._controller.set_inhibit(not self._controller.inhibited)
+ def fs_bypass_toggle_item_cb(self, widget, data=None):
+ """Callback when a request to toggle fullscreen bypass was made.
+
+ This ensures that the state of redshift is synchronised with
+ the toggle state of the widget (e.g. Gtk.CheckMenuItem).
+ """
+ active = not self._controller.fs_bypass_inhibited
+ if active != widget.get_active():
+ self._controller.set_fs_bypass_inhibit(
+ not self._controller.fs_bypass_inhibited)
+
# Info dialog callbacks
def show_info_cb(self, widget, data=None):
"""Callback when the info dialog should be presented."""
@@ -276,6 +297,10 @@ def inhibit_change_cb(self, controller, inhibit):
"""Callback when controller changes inhibition status."""
self.change_inhibited(inhibit)
+ def fs_bypass_inhibit_change_cb(self, controller, fs_bypass_inhibit):
+ """Callback when controller changes inhibition status."""
+ self.change_fs_bypass_inhibited(fs_bypass_inhibit)
+
def period_change_cb(self, controller, period):
"""Callback when controller changes period."""
self.change_period(period)
@@ -313,6 +338,13 @@ def change_inhibited(self, inhibited):
_('Status: {}').format(
_('Disabled') if inhibited else _('Enabled')))
+ def change_fs_bypass_inhibited(self, fs_bypass_inhibited):
+ """Change interface to new fullscreen bypass inhibition status."""
+ self.fs_bypass_toggle_item.set_active(not fs_bypass_inhibited)
+ self.status_label.set_markup(
+ _('Fullscreen Bypass: {}').format(
+ _('Disabled') if fs_bypass_inhibited else _('Enabled')))
+
def change_temperature(self, temperature):
"""Change interface to new temperature."""
self.temperature_label.set_markup(
diff --git a/src/redshift.c b/src/redshift.c
index d2ba577c..f1885fe9 100644
--- a/src/redshift.c
+++ b/src/redshift.c
@@ -66,6 +66,7 @@ int poll(struct pollfd *fds, int nfds, int timeout) { abort(); return -1; }
#include "hooks.h"
#include "signals.h"
#include "options.h"
+#include "fullscreen.h"
/* pause() is not defined on windows platform but is not needed either.
Use a noop macro instead. */
@@ -608,7 +609,7 @@ run_continual_mode(const location_provider_t *provider,
const transition_scheme_t *scheme,
const gamma_method_t *method,
gamma_state_t *method_state,
- int use_fade, int preserve_gamma, int verbose)
+ int use_fade, int preserve_gamma, int fullscreen_check, int verbose)
{
int r;
@@ -662,11 +663,18 @@ run_continual_mode(const location_provider_t *provider,
printf(_("Brightness: %.2f\n"), interp.brightness);
}
+ if (fullscreen_check) {
+ fullscreen.init();
+ }
+
/* Continuously adjust color temperature */
int done = 0;
int prev_disabled = 1;
int disabled = 0;
int location_available = 1;
+ int prev_fs_bypass_disabled = 1;
+ int fs_bypass_disabled = 0;
+ int is_fullscreen = 0;
while (1) {
/* Check to see if disable signal was caught */
if (disable && !done) {
@@ -674,6 +682,12 @@ run_continual_mode(const location_provider_t *provider,
disable = 0;
}
+ /* Check to see if fs_disable signal was caught */
+ if (fs_bypass_disable && !done){
+ fs_bypass_disabled = !fs_bypass_disabled;
+ fs_bypass_disable = 0;
+ }
+
/* Check to see if exit signal was caught */
if (exiting) {
if (done) {
@@ -686,13 +700,26 @@ run_continual_mode(const location_provider_t *provider,
exiting = 0;
}
+ if (fullscreen.check() && !done && !fs_bypass_disabled) {
+ is_fullscreen = 1;
+ } else {
+ is_fullscreen = 0;
+ }
+
/* Print status change */
if (verbose && disabled != prev_disabled) {
printf(_("Status: %s\n"), disabled ?
_("Disabled") : _("Enabled"));
}
+ /* Print fullscreen bypass enable/disable change */
+ if (verbose && fs_bypass_disabled != prev_fs_bypass_disabled) {
+ printf(_("Fullscreen bypass: %s\n"), fs_bypass_disabled ?
+ _("Disabled") : _("Enabled"));
+ }
+
prev_disabled = disabled;
+ prev_fs_bypass_disabled = fs_bypass_disabled;
/* Read timestamp */
double now;
@@ -727,7 +754,7 @@ run_continual_mode(const location_provider_t *provider,
interpolate_transition_scheme(
scheme, transition_prog, &target_interp);
- if (disabled) {
+ if (disabled || is_fullscreen) {
period = PERIOD_NONE;
color_setting_reset(&target_interp);
}
@@ -1308,6 +1335,7 @@ main(int argc, char *argv[])
options.provider, location_state, scheme,
options.method, method_state,
options.use_fade, options.preserve_gamma,
+ options.fullscreen_check,
options.verbose);
if (r < 0) exit(EXIT_FAILURE);
}
diff --git a/src/redshift.h b/src/redshift.h
index 0282d839..242a3966 100644
--- a/src/redshift.h
+++ b/src/redshift.h
@@ -150,4 +150,18 @@ typedef struct {
} location_provider_t;
+/* Fullscreen detector */
+typedef int fullscreen_init_func();
+typedef int fullscreen_check_func();
+
+typedef struct {
+ char *name;
+
+ /* Initialize display. */
+ fullscreen_init_func *init;
+
+ /* Check if active window is fullscreen. */
+ fullscreen_check_func *check;
+} fullscreen_t;
+
#endif /* ! REDSHIFT_REDSHIFT_H */
diff --git a/src/signals.c b/src/signals.c
index cee5ece1..b4af48b3 100644
--- a/src/signals.c
+++ b/src/signals.c
@@ -34,6 +34,7 @@
volatile sig_atomic_t exiting = 0;
volatile sig_atomic_t disable = 0;
+volatile sig_atomic_t fs_bypass_disable = 0;
/* Signal handler for exit signals */
@@ -50,10 +51,18 @@ sigdisable(int signo)
disable = 1;
}
+/* Signal handler for disabling the fullscreen detector signal */
+static void
+sigfsbypassdisable(int signo)
+{
+ fs_bypass_disable = 1;
+}
+
#else /* ! HAVE_SIGNAL_H || __WIN32__ */
int disable = 0;
int exiting = 0;
+int fs_bypass_disable = 0;
#endif /* ! HAVE_SIGNAL_H || __WIN32__ */
@@ -95,6 +104,17 @@ signals_install_handlers(void)
return -1;
}
+ /* Install signal handler for USR2 signal */
+ sigact.sa_handler = sigfsbypassdisable;
+ sigact.sa_mask = sigset;
+ sigact.sa_flags = 0;
+
+ r = sigaction(SIGUSR2, &sigact, NULL);
+ if (r < 0) {
+ perror("sigaction");
+ return -1;
+ }
+
/* Ignore CHLD signal. This causes child processes
(hooks) to be reaped automatically. */
sigact.sa_handler = SIG_IGN;
diff --git a/src/signals.h b/src/signals.h
index 7a1d22ee..3c65d79c 100644
--- a/src/signals.h
+++ b/src/signals.h
@@ -25,10 +25,12 @@
extern volatile sig_atomic_t exiting;
extern volatile sig_atomic_t disable;
+extern volatile sig_atomic_t fs_bypass_disable;
#else /* ! HAVE_SIGNAL_H || __WIN32__ */
extern int exiting;
extern int disable;
+extern int fs_bypass_disable;
#endif /* ! HAVE_SIGNAL_H || __WIN32__ */