Merge pull request #472 from bebehei/fullscreen

Implement hiding on fullscreen
This commit is contained in:
Benedikt Heine 2018-02-25 20:50:54 +01:00 committed by GitHub
commit f80e6fc579
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 265 additions and 16 deletions

View File

@ -2,6 +2,10 @@
## Unreleased
### Added
- `fullscreen` rule to hide notifications when a fullscreen window is active
## 1.3.0 - 2018-01-05
### Added

View File

@ -594,8 +594,9 @@ Shell-like globing is supported.
=item B<modifying>
The following attributes can be overridden: timeout, urgency, foreground,
background, new_icon, set_transient, format where, as with the filtering attributes,
each one corresponds to the respective notification attribute to be modified.
background, new_icon, set_transient, format, fullscreen where,
as with the filtering attributes, each one corresponds to the respective
notification attribute to be modified.
As with filtering, to make a rule modify an attribute simply assign it in the
rule definition.

15
dunstrc
View File

@ -279,7 +279,7 @@
# override settings for certain messages.
# Messages can be matched by "appname", "summary", "body", "icon", "category",
# "msg_urgency" and you can override the "timeout", "urgency", "foreground",
# "background", "new_icon" and "format".
# "background", "new_icon" and "format", "fullscreen".
# Shell-like globbing will get expanded.
#
# SCRIPTING
@ -294,6 +294,19 @@
# NOTE: It might be helpful to run dunst -print in a terminal in order
# to find fitting options for rules.
# fullscreen values
# show: show the notifications, regardless if there is a fullscreen window opened
# delay: displays the new notification, if there is there is no fullscreen window active
# If the notification is already drawn, it won't get undrawn.
# pushback: same as delay, but when switching into fullscreen, the notification will get
# withdrawn from screen again and will get delayed like a new notification
#[fullscreen_delay_everything]
# fullscreen = delay
#[fullscreen_show_critical]
# msg_urgency = critical
# fullscreen = show
#[espeak]
# summary = "*"
# script = dunst_espeak.sh

View File

@ -48,8 +48,12 @@ void wake_up(void)
static gboolean run(void *data)
{
queues_check_timeouts(x_is_idle());
queues_update();
LOG_D("RUN");
bool fullscreen = have_fullscreen_window();
queues_check_timeouts(x_is_idle(), fullscreen);
queues_update(fullscreen);
static gint64 next_timeout = 0;

View File

@ -28,6 +28,19 @@ static void notification_extract_urls(notification *n);
static void notification_format_message(notification *n);
static void notification_dmenu_string(notification *n);
/* see notification.h */
const char *enum_to_string_fullscreen(enum behavior_fullscreen in)
{
switch (in) {
case FS_SHOW: return "show";
case FS_DELAY: return "delay";
case FS_PUSHBACK: return "pushback";
case FS_NULL: return "(null)";
default:
LOG_E("Enum behavior_fullscreen has wrong value.");
}
}
/*
* print a human readable representation
* of the given notification to stdout.
@ -49,6 +62,7 @@ void notification_print(notification *n)
printf("\tfg: %s\n", n->colors[ColFG]);
printf("\tbg: %s\n", n->colors[ColBG]);
printf("\tframe: %s\n", n->colors[ColFrame]);
printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
printf("\tid: %d\n", n->id);
if (n->urls) {
char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls));
@ -284,6 +298,8 @@ notification *notification_create(void)
n->transient = false;
n->progress = -1;
n->fullscreen = FS_SHOW;
return n;
}

View File

@ -9,6 +9,13 @@
#define DUNST_NOTIF_MAX_CHARS 5000
enum behavior_fullscreen {
FS_NULL, //!< Invalid value
FS_DELAY, //!< Delay the notification until leaving fullscreen mode
FS_PUSHBACK, //!< When entering fullscreen mode, push the notification back to waiting
FS_SHOW, //!< Show the message when in fullscreen mode
};
/// Representing the urgencies according to the notification spec
enum urgency {
URG_NONE = -1, /**< Urgency not set (invalid) */
@ -69,6 +76,7 @@ typedef struct _notification {
bool first_render; /**< markup has been rendered before? */
int dup_count; /**< amount of duplicate notifications stacked onto this */
int displayed_height;
enum behavior_fullscreen fullscreen; //!< The instruction what to do with it, when desktop enters fullscreen
/* derived fields */
char *msg; /**< formatted message */
@ -94,5 +102,14 @@ void notification_update_text_to_render(notification *n);
void notification_do_action(notification *n);
const char *notification_urgency_to_string(enum urgency urgency);
/**
* Return the string representation for fullscreen behavior
*
* @param in the #behavior_fullscreen enum value to represent
* @return the string representation for `in`
*/
const char *enum_to_string_fullscreen(enum behavior_fullscreen in);
#endif
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -551,4 +551,22 @@ const char *cmdline_create_usage(void)
return usage_str;
}
/* see option_parser.h */
enum behavior_fullscreen parse_enum_fullscreen(const char *string, enum behavior_fullscreen def)
{
if (!string)
return def;
if (strcmp(string, "show") == 0)
return FS_SHOW;
else if (strcmp(string, "delay") == 0)
return FS_DELAY;
else if (strcmp(string, "pushback") == 0)
return FS_PUSHBACK;
else {
LOG_W("Unknown fullscreen value: '%s'\n", string);
return def;
}
}
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -6,6 +6,8 @@
#include <stdbool.h>
#include <stdio.h>
#include "dunst.h"
int load_ini_file(FILE *);
char *ini_get_path(const char *section, const char *key, const char *def);
char *ini_get_string(const char *section, const char *key, const char *def);
@ -63,5 +65,17 @@ int option_get_bool(const char *ini_section,
*/
const char *next_section(const char *section);
/**
* Parse the fullscreen behavior value of the given string
*
* @param string the string representation of #behavior_fullscreen.
* The string must not contain any waste characters.
* @param def value to return in case of errors
*
* @return the #behavior_fullscreen representation of `string`
* @return `def` if `string` is invalid or `NULL`
*/
enum behavior_fullscreen parse_enum_fullscreen(const char *string, enum behavior_fullscreen def);
#endif
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -284,12 +284,14 @@ void queues_history_push_all(void)
}
/* see queues.h */
void queues_check_timeouts(bool idle)
void queues_check_timeouts(bool idle, bool fullscreen)
{
/* nothing to do */
if (displayed->length == 0)
return;
bool is_idle = fullscreen ? false : idle;
GList *iter = g_queue_peek_head_link(displayed);
while (iter) {
notification *n = iter->data;
@ -302,7 +304,7 @@ void queues_check_timeouts(bool idle)
iter = iter->next;
/* don't timeout when user is idle */
if (idle && !n->transient) {
if (is_idle && !n->transient) {
n->start = g_get_monotonic_time();
continue;
}
@ -320,7 +322,7 @@ void queues_check_timeouts(bool idle)
}
/* see queues.h */
void queues_update(void)
void queues_update(bool fullscreen)
{
if (pause_displayed) {
while (displayed->length > 0) {
@ -330,26 +332,52 @@ void queues_update(void)
return;
}
/* move notifications back to queue, which are set to pushback */
if (fullscreen) {
GList *iter = g_queue_peek_head_link(displayed);
while (iter) {
notification *n = iter->data;
GList *nextiter = iter->next;
if (n->fullscreen == FS_PUSHBACK){
g_queue_delete_link(displayed, iter);
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
}
iter = nextiter;
}
}
/* move notifications from queue to displayed */
while (waiting->length > 0) {
GList *iter = g_queue_peek_head_link(waiting);
while (iter) {
notification *n = iter->data;
GList *nextiter = iter->next;
if (displayed_limit > 0 && displayed->length >= displayed_limit) {
/* the list is full */
break;
}
notification *n = g_queue_pop_head(waiting);
if (!n)
return;
if (fullscreen
&& (n->fullscreen == FS_DELAY || n->fullscreen == FS_PUSHBACK)) {
iter = nextiter;
continue;
}
n->start = g_get_monotonic_time();
if (!n->redisplayed && n->script) {
notification_run_script(n);
}
g_queue_delete_link(waiting, iter);
g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL);
iter = nextiter;
}
}

View File

@ -125,8 +125,10 @@ void queues_history_push_all(void);
*
* @param idle the program's idle status. Important to calculate the
* timeout for transient notifications
* @param fullscreen the desktop's fullscreen status. Important to
* calculate the timeout for transient notifications
*/
void queues_check_timeouts(bool idle);
void queues_check_timeouts(bool idle, bool fullscreen);
/**
* Move inserted notifications from waiting queue to displayed queue
@ -135,8 +137,11 @@ void queues_check_timeouts(bool idle);
*
* @post Call wake_up() to synchronize the queues with the UI
* (which closes old and shows new notifications on screen)
*
* @param fullscreen the desktop's fullscreen status. Important to
* move notifications to the right queue
*/
void queues_update(void);
void queues_update(bool fullscreen);
/**
* Calculate the distance to the next event, when an element in the

View File

@ -16,6 +16,8 @@ void rule_apply(rule_t *r, notification *n)
n->timeout = r->timeout;
if (r->urgency != URG_NONE)
n->urgency = r->urgency;
if (r->fullscreen != FS_NULL)
n->fullscreen = r->fullscreen;
if (r->history_ignore != -1)
n->history_ignore = r->history_ignore;
if (r->set_transient != -1)
@ -69,6 +71,7 @@ void rule_init(rule_t *r)
r->msg_urgency = URG_NONE;
r->timeout = -1;
r->urgency = URG_NONE;
r->fullscreen = FS_NULL;
r->markup = MARKUP_NULL;
r->new_icon = NULL;
r->history_ignore = false;

View File

@ -30,6 +30,7 @@ typedef struct _rule_t {
char *bg;
const char *format;
const char *script;
enum behavior_fullscreen fullscreen;
} rule_t;
extern GSList *rules;

View File

@ -684,6 +684,15 @@ void load_settings(char *cmdline_config_path)
r->history_ignore = ini_get_bool(cur_section, "history_ignore", r->history_ignore);
r->match_transient = ini_get_bool(cur_section, "match_transient", r->match_transient);
r->set_transient = ini_get_bool(cur_section, "set_transient", r->set_transient);
{
char *c = ini_get_string(
cur_section,
"fullscreen", NULL
);
r->fullscreen = parse_enum_fullscreen(c, r->fullscreen);
g_free(c);
}
r->script = ini_get_path(cur_section, "script", NULL);
}

View File

@ -146,6 +146,8 @@ void screen_check_event(XEvent event)
{
if (event.type == randr_event_base + RRScreenChangeNotify)
randr_update();
else
LOG_D("XEvent: Ignored '%d'", event.type);
}
void xinerama_update(void)
@ -186,6 +188,89 @@ void screen_update_fallback(void)
screens[0].dim.h = DisplayHeight(xctx.dpy, screen);
}
/* see screen.h */
bool have_fullscreen_window(void)
{
return window_is_fullscreen(get_focused_window());
}
/**
* X11 ErrorHandler to mainly discard BadWindow parameter error
*/
static int XErrorHandlerFullscreen(Display *display, XErrorEvent *e)
{
/* Ignore BadWindow errors. Window may have been gone */
if (e->error_code == BadWindow) {
return 0;
}
char err_buf[BUFSIZ];
XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
fputs(err_buf, stderr);
fputs("\n", stderr);
return 0;
}
/* see screen.h */
bool window_is_fullscreen(Window window)
{
bool fs = false;
if (!window)
return false;
Atom has_wm_state = XInternAtom(xctx.dpy, "_NET_WM_STATE", True);
if (has_wm_state == None){
return false;
}
XFlush(xctx.dpy);
XSetErrorHandler(XErrorHandlerFullscreen);
Atom actual_type_return;
int actual_format_return;
unsigned long bytes_after_return;
unsigned char *prop_to_return;
unsigned long n_items;
int result = XGetWindowProperty(
xctx.dpy,
window,
has_wm_state,
0, /* long_offset */
sizeof(window), /* long_length */
false, /* delete */
AnyPropertyType, /* req_type */
&actual_type_return,
&actual_format_return,
&n_items,
&bytes_after_return,
&prop_to_return);
XFlush(xctx.dpy);
XSync(xctx.dpy, false);
XSetErrorHandler(NULL);
if (result == Success) {
for(int i = 0; i < n_items; i++) {
char *atom = XGetAtomName(xctx.dpy, ((Atom*)prop_to_return)[i]);
if (atom) {
if(0 == strcmp("_NET_WM_STATE_FULLSCREEN", atom))
fs = true;
XFree(atom);
if(fs)
break;
}
}
}
if (prop_to_return)
XFree(prop_to_return);
return fs;
}
/*
* Select the screen on which the Window
* should be displayed.

View File

@ -3,6 +3,7 @@
#define DUNST_SCREEN_H
#include <X11/Xlib.h>
#include <stdbool.h>
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
@ -27,5 +28,24 @@ void screen_check_event(XEvent event);
screen_info *get_active_screen(void);
double get_dpi_for_screen(screen_info *scr);
/**
* Find the currently focused window and check if it's in
* fullscreen mode
*
* @see window_is_fullscreen()
* @see get_focused_window()
*
* @return `true` if the focused window is in fullscreen mode
*/
bool have_fullscreen_window(void);
/**
* Check if window is in fullscreen mode
*
* @param window the x11 window object
* @return `true` if `window` is in fullscreen mode
*/
bool window_is_fullscreen(Window window);
#endif
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -60,6 +60,7 @@ typedef struct _colored_layout {
} colored_layout;
cairo_ctx_t cairo_ctx;
static bool fullscreen_last = false;
/* FIXME refactor setup teardown handlers into one setup and one teardown */
static void x_shortcut_setup_error_handler(void);
@ -848,10 +849,13 @@ gboolean x_mainloop_fd_check(GSource *source)
*/
gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
bool fullscreen_now;
XEvent ev;
unsigned int state;
while (XPending(xctx.dpy) > 0) {
XNextEvent(xctx.dpy, &ev);
LOG_D("XEvent: processing '%d'", ev.type);
switch (ev.type) {
case Expose:
if (ev.xexpose.count == 0 && xctx.visible) {
@ -908,13 +912,20 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer
wake_up();
break;
case PropertyNotify:
fullscreen_now = have_fullscreen_window();
if (fullscreen_now != fullscreen_last) {
fullscreen_last = fullscreen_now;
wake_up();
} else if ( settings.f_mode != FOLLOW_NONE
/* Ignore PropertyNotify, when we're still on the
* same screen. PropertyNotify is only neccessary
* to detect a focus change to another screen
*/
if( settings.f_mode != FOLLOW_NONE
&& get_active_screen()->scr != xctx.cur_screen)
wake_up();
&& xctx.visible
&& get_active_screen()->scr != xctx.cur_screen) {
x_win_draw();
}
break;
default:
screen_check_event(ev);