|
|
14ad8e |
From 60d38a7c6683001ee2beb72b8f0b0beee4f04bb4 Mon Sep 17 00:00:00 2001
|
|
|
14ad8e |
From: "Owen W. Taylor" <otaylor@fishsoup.net>
|
|
|
14ad8e |
Date: Wed, 21 Oct 2009 18:07:12 -0400
|
|
|
14ad8e |
Subject: [PATCH] Add no-focus-windows preference to list windows that
|
|
|
14ad8e |
shouldn't be focused
|
|
|
14ad8e |
|
|
|
14ad8e |
Notification windows from legacy software that don't set _NET_WM_USER_TIME
|
|
|
14ad8e |
can be a huge annoyance for users, since they will pop up and steal focus.
|
|
|
14ad8e |
|
|
|
14ad8e |
Add:
|
|
|
14ad8e |
|
|
|
14ad8e |
no-focus-windows
|
|
|
14ad8e |
|
|
|
14ad8e |
which is a list of expressions identifying new windows that shouldn't ever
|
|
|
14ad8e |
be focused. For example:
|
|
|
14ad8e |
|
|
|
14ad8e |
(and (eq class 'Mylegacyapp') (glob name 'New mail*'))
|
|
|
14ad8e |
|
|
|
14ad8e |
https://bugzilla.gnome.org/show_bug.cgi?id=599248
|
|
|
14ad8e |
---
|
|
|
14ad8e |
src/Makefile.am | 2 +
|
|
|
14ad8e |
src/core/prefs.c | 55 +++
|
|
|
14ad8e |
src/core/window-matcher.c | 582 +++++++++++++++++++++++++++++++++
|
|
|
14ad8e |
src/core/window-matcher.h | 46 +++
|
|
|
14ad8e |
src/core/window.c | 9 +-
|
|
|
14ad8e |
src/include/prefs.h | 6 +-
|
|
|
14ad8e |
src/metacity-schemas.convert | 1 +
|
|
|
14ad8e |
src/org.gnome.metacity.gschema.xml.in | 21 ++
|
|
|
14ad8e |
8 files changed, 720 insertions(+), 2 deletions(-)
|
|
|
14ad8e |
create mode 100644 src/core/window-matcher.c
|
|
|
14ad8e |
create mode 100644 src/core/window-matcher.h
|
|
|
14ad8e |
|
|
|
14ad8e |
diff --git a/src/Makefile.am b/src/Makefile.am
|
|
|
14ad8e |
index 4d405bf..2befe33 100644
|
|
|
14ad8e |
--- a/src/Makefile.am
|
|
|
14ad8e |
+++ b/src/Makefile.am
|
|
|
14ad8e |
@@ -66,6 +66,8 @@ metacity_SOURCES= \
|
|
|
14ad8e |
core/stack.h \
|
|
|
14ad8e |
core/util.c \
|
|
|
14ad8e |
include/util.h \
|
|
|
14ad8e |
+ core/window-matcher.c \
|
|
|
14ad8e |
+ core/window-matcher.h \
|
|
|
14ad8e |
core/window-props.c \
|
|
|
14ad8e |
core/window-props.h \
|
|
|
14ad8e |
core/window.c \
|
|
|
14ad8e |
diff --git a/src/core/prefs.c b/src/core/prefs.c
|
|
|
14ad8e |
index 58f11e9..24a98cd 100644
|
|
|
14ad8e |
--- a/src/core/prefs.c
|
|
|
14ad8e |
+++ b/src/core/prefs.c
|
|
|
14ad8e |
@@ -26,6 +26,7 @@
|
|
|
14ad8e |
|
|
|
14ad8e |
#include <config.h>
|
|
|
14ad8e |
#include "prefs.h"
|
|
|
14ad8e |
+#include "window-matcher.h"
|
|
|
14ad8e |
#include "ui.h"
|
|
|
14ad8e |
#include "util.h"
|
|
|
14ad8e |
#include <glib.h>
|
|
|
14ad8e |
@@ -70,6 +71,7 @@ static PangoFontDescription *titlebar_font = NULL;
|
|
|
14ad8e |
static MetaVirtualModifier mouse_button_mods = Mod1Mask;
|
|
|
14ad8e |
static GDesktopFocusMode focus_mode = G_DESKTOP_FOCUS_MODE_CLICK;
|
|
|
14ad8e |
static GDesktopFocusNewWindows focus_new_windows = G_DESKTOP_FOCUS_NEW_WINDOWS_SMART;
|
|
|
14ad8e |
+static GSList *no_focus_windows = NULL;
|
|
|
14ad8e |
static gboolean raise_on_click = TRUE;
|
|
|
14ad8e |
static char* current_theme = NULL;
|
|
|
14ad8e |
static int num_workspaces = 4;
|
|
|
14ad8e |
@@ -120,6 +122,7 @@ static void maybe_give_disable_workarounds_warning (void);
|
|
|
14ad8e |
|
|
|
14ad8e |
static gboolean titlebar_handler (GVariant*, gpointer*, gpointer);
|
|
|
14ad8e |
static gboolean theme_name_handler (GVariant*, gpointer*, gpointer);
|
|
|
14ad8e |
+static gboolean no_focus_windows_handler (GVariant*, gpointer*, gpointer);
|
|
|
14ad8e |
static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer);
|
|
|
14ad8e |
static gboolean button_layout_handler (GVariant*, gpointer*, gpointer);
|
|
|
14ad8e |
|
|
|
14ad8e |
@@ -367,6 +370,14 @@ static MetaStringPreference preferences_string[] =
|
|
|
14ad8e |
NULL,
|
|
|
14ad8e |
},
|
|
|
14ad8e |
{
|
|
|
14ad8e |
+ { "no-focus-windows",
|
|
|
14ad8e |
+ SCHEMA_METACITY,
|
|
|
14ad8e |
+ META_PREF_NO_FOCUS_WINDOWS,
|
|
|
14ad8e |
+ },
|
|
|
14ad8e |
+ no_focus_windows_handler,
|
|
|
14ad8e |
+ NULL
|
|
|
14ad8e |
+ },
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
{ KEY_TITLEBAR_FONT,
|
|
|
14ad8e |
SCHEMA_GENERAL,
|
|
|
14ad8e |
META_PREF_TITLEBAR_FONT,
|
|
|
14ad8e |
@@ -998,6 +1009,39 @@ theme_name_handler (GVariant *value,
|
|
|
14ad8e |
}
|
|
|
14ad8e |
|
|
|
14ad8e |
static gboolean
|
|
|
14ad8e |
+no_focus_windows_handler (GVariant *value,
|
|
|
14ad8e |
+ gpointer *result,
|
|
|
14ad8e |
+ gpointer data)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ const gchar *string_value;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ *result = NULL; /* ignored */
|
|
|
14ad8e |
+ string_value = g_variant_get_string (value, NULL);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if (no_focus_windows)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ meta_window_matcher_list_free (no_focus_windows);
|
|
|
14ad8e |
+ no_focus_windows = NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if (string_value)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ GError *error = NULL;
|
|
|
14ad8e |
+ no_focus_windows = meta_window_matcher_list_from_string (string_value, &error);
|
|
|
14ad8e |
+ if (error != NULL)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ meta_warning ("Error parsing no_focus_windows='%s': %s\n",
|
|
|
14ad8e |
+ string_value, error->message);
|
|
|
14ad8e |
+ g_error_free (error);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return FALSE;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return TRUE;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static gboolean
|
|
|
14ad8e |
mouse_button_mods_handler (GVariant *value,
|
|
|
14ad8e |
gpointer *result,
|
|
|
14ad8e |
gpointer data)
|
|
|
14ad8e |
@@ -1414,6 +1458,9 @@ meta_preference_to_string (MetaPreference pref)
|
|
|
14ad8e |
|
|
|
14ad8e |
case META_PREF_FORCE_FULLSCREEN:
|
|
|
14ad8e |
return "FORCE_FULLSCREEN";
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ case META_PREF_NO_FOCUS_WINDOWS:
|
|
|
14ad8e |
+ return "NO_FOCUS_WINDOWS";
|
|
|
14ad8e |
}
|
|
|
14ad8e |
|
|
|
14ad8e |
return "(unknown)";
|
|
|
14ad8e |
@@ -1710,6 +1757,14 @@ meta_prefs_get_action_right_click_titlebar (void)
|
|
|
14ad8e |
}
|
|
|
14ad8e |
|
|
|
14ad8e |
gboolean
|
|
|
14ad8e |
+meta_prefs_window_is_no_focus (const char *window_name,
|
|
|
14ad8e |
+ const char *window_class)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ return meta_window_matcher_list_matches (no_focus_windows,
|
|
|
14ad8e |
+ window_name, window_class);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+gboolean
|
|
|
14ad8e |
meta_prefs_get_auto_raise (void)
|
|
|
14ad8e |
{
|
|
|
14ad8e |
return auto_raise;
|
|
|
14ad8e |
diff --git a/src/core/window-matcher.c b/src/core/window-matcher.c
|
|
|
14ad8e |
new file mode 100644
|
|
|
14ad8e |
index 0000000..df889eb
|
|
|
14ad8e |
--- /dev/null
|
|
|
14ad8e |
+++ b/src/core/window-matcher.c
|
|
|
14ad8e |
@@ -0,0 +1,582 @@
|
|
|
14ad8e |
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+/* Tiny language for matching against windows */
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+/*
|
|
|
14ad8e |
+ * Copyright (C) 2009 Red Hat, Inc.
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * This program is free software; you can redistribute it and/or
|
|
|
14ad8e |
+ * modify it under the terms of the GNU General Public License as
|
|
|
14ad8e |
+ * published by the Free Software Foundation; either version 2 of the
|
|
|
14ad8e |
+ * License, or (at your option) any later version.
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * This program is distributed in the hope that it will be useful, but
|
|
|
14ad8e |
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
14ad8e |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
14ad8e |
+ * General Public License for more details.
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * You should have received a copy of the GNU General Public License
|
|
|
14ad8e |
+ * along with this program; if not, write to the Free Software
|
|
|
14ad8e |
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
14ad8e |
+ * 02111-1307, USA.
|
|
|
14ad8e |
+ */
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+#include <glib.h>
|
|
|
14ad8e |
+#include <string.h>
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+#include "window-matcher.h"
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+typedef struct _MetaWindowMatcher MetaWindowMatcher;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+typedef enum {
|
|
|
14ad8e |
+ MATCHER_OPERAND_CLASS,
|
|
|
14ad8e |
+ MATCHER_OPERAND_NAME
|
|
|
14ad8e |
+} MatcherOperand;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+typedef enum {
|
|
|
14ad8e |
+ MATCHER_TOKEN_AND = G_TOKEN_LAST + 1,
|
|
|
14ad8e |
+ MATCHER_TOKEN_OR,
|
|
|
14ad8e |
+ MATCHER_TOKEN_NOT,
|
|
|
14ad8e |
+ MATCHER_TOKEN_EQ,
|
|
|
14ad8e |
+ MATCHER_TOKEN_GLOB,
|
|
|
14ad8e |
+ MATCHER_TOKEN_NAME,
|
|
|
14ad8e |
+ MATCHER_TOKEN_CLASS
|
|
|
14ad8e |
+} MatcherToken;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+struct _MetaWindowMatcher {
|
|
|
14ad8e |
+ enum {
|
|
|
14ad8e |
+ MATCHER_AND,
|
|
|
14ad8e |
+ MATCHER_OR,
|
|
|
14ad8e |
+ MATCHER_NOT,
|
|
|
14ad8e |
+ MATCHER_EQ,
|
|
|
14ad8e |
+ MATCHER_GLOB
|
|
|
14ad8e |
+ } type;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ union {
|
|
|
14ad8e |
+ struct {
|
|
|
14ad8e |
+ MetaWindowMatcher *a;
|
|
|
14ad8e |
+ MetaWindowMatcher *b;
|
|
|
14ad8e |
+ } and;
|
|
|
14ad8e |
+ struct {
|
|
|
14ad8e |
+ MetaWindowMatcher *a;
|
|
|
14ad8e |
+ MetaWindowMatcher *b;
|
|
|
14ad8e |
+ } or;
|
|
|
14ad8e |
+ struct {
|
|
|
14ad8e |
+ MetaWindowMatcher *a;
|
|
|
14ad8e |
+ } not;
|
|
|
14ad8e |
+ struct {
|
|
|
14ad8e |
+ MatcherOperand operand;
|
|
|
14ad8e |
+ char *str;
|
|
|
14ad8e |
+ } eq;
|
|
|
14ad8e |
+ struct {
|
|
|
14ad8e |
+ MatcherOperand operand;
|
|
|
14ad8e |
+ char *str;
|
|
|
14ad8e |
+ GPatternSpec *pattern;
|
|
|
14ad8e |
+ } glob;
|
|
|
14ad8e |
+ } u;
|
|
|
14ad8e |
+};
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+meta_window_matcher_free (MetaWindowMatcher *matcher)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ switch (matcher->type)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_AND:
|
|
|
14ad8e |
+ meta_window_matcher_free (matcher->u.and.a);
|
|
|
14ad8e |
+ meta_window_matcher_free (matcher->u.and.b);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_OR:
|
|
|
14ad8e |
+ meta_window_matcher_free (matcher->u.or.a);
|
|
|
14ad8e |
+ meta_window_matcher_free (matcher->u.or.b);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_NOT:
|
|
|
14ad8e |
+ meta_window_matcher_free (matcher->u.or.a);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_EQ:
|
|
|
14ad8e |
+ g_free (matcher->u.eq.str);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_GLOB:
|
|
|
14ad8e |
+ g_free (matcher->u.glob.str);
|
|
|
14ad8e |
+ g_pattern_spec_free (matcher->u.glob.pattern);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_slice_free (MetaWindowMatcher, matcher);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+void
|
|
|
14ad8e |
+meta_window_matcher_list_free (GSList *list)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ g_slist_foreach (list, (GFunc)meta_window_matcher_free, NULL);
|
|
|
14ad8e |
+ g_slist_free (list);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static gboolean
|
|
|
14ad8e |
+meta_window_matcher_matches (MetaWindowMatcher *matcher,
|
|
|
14ad8e |
+ const char *window_name,
|
|
|
14ad8e |
+ const char *window_class)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ switch (matcher->type)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_AND:
|
|
|
14ad8e |
+ return (meta_window_matcher_matches (matcher->u.and.a, window_name, window_class) &&
|
|
|
14ad8e |
+ meta_window_matcher_matches (matcher->u.and.b, window_name, window_class));
|
|
|
14ad8e |
+ case MATCHER_OR:
|
|
|
14ad8e |
+ return (meta_window_matcher_matches (matcher->u.or.a, window_name, window_class) ||
|
|
|
14ad8e |
+ meta_window_matcher_matches(matcher->u.or.b, window_name, window_class));
|
|
|
14ad8e |
+ case MATCHER_NOT:
|
|
|
14ad8e |
+ return !meta_window_matcher_matches (matcher->u.not.a, window_name, window_class);
|
|
|
14ad8e |
+ case MATCHER_EQ:
|
|
|
14ad8e |
+ if (matcher->u.eq.operand == MATCHER_OPERAND_NAME)
|
|
|
14ad8e |
+ return window_name && strcmp (matcher->u.eq.str, window_name) == 0;
|
|
|
14ad8e |
+ else
|
|
|
14ad8e |
+ return window_class && strcmp (matcher->u.eq.str, window_class) == 0;
|
|
|
14ad8e |
+ case MATCHER_GLOB:
|
|
|
14ad8e |
+ if (matcher->u.glob.operand == MATCHER_OPERAND_NAME)
|
|
|
14ad8e |
+ return window_name && g_pattern_match_string (matcher->u.glob.pattern, window_name);
|
|
|
14ad8e |
+ else
|
|
|
14ad8e |
+ return window_class && g_pattern_match_string (matcher->u.glob.pattern, window_class);
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_assert_not_reached();
|
|
|
14ad8e |
+ return FALSE;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+gboolean
|
|
|
14ad8e |
+meta_window_matcher_list_matches (GSList *list,
|
|
|
14ad8e |
+ const char *window_name,
|
|
|
14ad8e |
+ const char *window_class)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ GSList *l;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ for (l = list; l; l = l->next)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ if (meta_window_matcher_matches (l->data, window_name, window_class))
|
|
|
14ad8e |
+ return TRUE;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return FALSE;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static const GScannerConfig scanner_config =
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ " \t\r\n" /* cset_skip_characters */,
|
|
|
14ad8e |
+ (
|
|
|
14ad8e |
+ G_CSET_a_2_z
|
|
|
14ad8e |
+ "_"
|
|
|
14ad8e |
+ G_CSET_A_2_Z
|
|
|
14ad8e |
+ ) /* cset_identifier_first */,
|
|
|
14ad8e |
+ (
|
|
|
14ad8e |
+ G_CSET_a_2_z
|
|
|
14ad8e |
+ "_"
|
|
|
14ad8e |
+ G_CSET_A_2_Z
|
|
|
14ad8e |
+ G_CSET_DIGITS
|
|
|
14ad8e |
+ G_CSET_LATINS
|
|
|
14ad8e |
+ G_CSET_LATINC
|
|
|
14ad8e |
+ ) /* cset_identifier_nth */,
|
|
|
14ad8e |
+ NULL /* cpair_comment_single */,
|
|
|
14ad8e |
+ TRUE /* case_sensitive */,
|
|
|
14ad8e |
+ TRUE /* skip_comment_multi */,
|
|
|
14ad8e |
+ FALSE /* skip_comment_single */,
|
|
|
14ad8e |
+ TRUE /* scan_comment_multi */,
|
|
|
14ad8e |
+ TRUE /* scan_identifier */,
|
|
|
14ad8e |
+ TRUE /* scan_identifier_1char */,
|
|
|
14ad8e |
+ FALSE /* scan_identifier_NULL */,
|
|
|
14ad8e |
+ TRUE /* scan_symbols */,
|
|
|
14ad8e |
+ FALSE /* scan_binary */,
|
|
|
14ad8e |
+ TRUE /* scan_octal */,
|
|
|
14ad8e |
+ TRUE /* scan_float */,
|
|
|
14ad8e |
+ TRUE /* scan_hex */,
|
|
|
14ad8e |
+ FALSE /* scan_hex_dollar */,
|
|
|
14ad8e |
+ TRUE /* scan_string_sq */,
|
|
|
14ad8e |
+ TRUE /* scan_string_dq */,
|
|
|
14ad8e |
+ TRUE /* numbers_2_int */,
|
|
|
14ad8e |
+ FALSE /* int_2_float */,
|
|
|
14ad8e |
+ FALSE /* identifier_2_string */,
|
|
|
14ad8e |
+ TRUE /* char_2_token */,
|
|
|
14ad8e |
+ TRUE /* symbol_2_token */,
|
|
|
14ad8e |
+ FALSE /* scope_0_fallback */,
|
|
|
14ad8e |
+ FALSE /* store_int64 */,
|
|
|
14ad8e |
+};
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+set_error (GScanner *scanner,
|
|
|
14ad8e |
+ GError **error,
|
|
|
14ad8e |
+ const char *message)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ g_set_error (error, 0, 0,
|
|
|
14ad8e |
+ "Parse error at %d:%d: %s",
|
|
|
14ad8e |
+ g_scanner_cur_line (scanner),
|
|
|
14ad8e |
+ g_scanner_cur_position (scanner),
|
|
|
14ad8e |
+ message);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static MetaWindowMatcher *
|
|
|
14ad8e |
+meta_window_matcher_new_and (MetaWindowMatcher *a,
|
|
|
14ad8e |
+ MetaWindowMatcher *b)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = g_slice_new0 (MetaWindowMatcher);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ matcher->type = MATCHER_AND;
|
|
|
14ad8e |
+ matcher->u.and.a = a;
|
|
|
14ad8e |
+ matcher->u.and.b = b;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return matcher;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static MetaWindowMatcher *
|
|
|
14ad8e |
+meta_window_matcher_new_or (MetaWindowMatcher *a,
|
|
|
14ad8e |
+ MetaWindowMatcher *b)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = g_slice_new0 (MetaWindowMatcher);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ matcher->type = MATCHER_OR;
|
|
|
14ad8e |
+ matcher->u.or.a = a;
|
|
|
14ad8e |
+ matcher->u.or.b = b;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return matcher;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static MetaWindowMatcher *
|
|
|
14ad8e |
+meta_window_matcher_new_not (MetaWindowMatcher *a)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = g_slice_new0 (MetaWindowMatcher);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ matcher->type = MATCHER_NOT;
|
|
|
14ad8e |
+ matcher->u.not.a = a;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return matcher;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static MetaWindowMatcher *
|
|
|
14ad8e |
+meta_window_matcher_new_eq (MatcherOperand operand,
|
|
|
14ad8e |
+ const char *str)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = g_slice_new0 (MetaWindowMatcher);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ matcher->type = MATCHER_EQ;
|
|
|
14ad8e |
+ matcher->u.eq.operand = operand;
|
|
|
14ad8e |
+ matcher->u.eq.str = g_strdup (str);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return matcher;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static MetaWindowMatcher *
|
|
|
14ad8e |
+meta_window_matcher_new_glob (MatcherOperand operand,
|
|
|
14ad8e |
+ const char *str)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = g_slice_new0 (MetaWindowMatcher);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ matcher->type = MATCHER_GLOB;
|
|
|
14ad8e |
+ matcher->u.glob.operand = operand;
|
|
|
14ad8e |
+ matcher->u.glob.str = g_strdup (str);
|
|
|
14ad8e |
+ matcher->u.glob.pattern = g_pattern_spec_new (str);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return matcher;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static MetaWindowMatcher *
|
|
|
14ad8e |
+meta_window_matcher_from_scanner (GScanner *scanner,
|
|
|
14ad8e |
+ GError **error)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = NULL;
|
|
|
14ad8e |
+ GTokenType token;
|
|
|
14ad8e |
+ GTokenValue value;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ token = g_scanner_get_next_token (scanner);
|
|
|
14ad8e |
+ if (token != G_TOKEN_LEFT_PAREN)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ set_error (scanner, error, "expected '('");
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ token = g_scanner_get_next_token (scanner);
|
|
|
14ad8e |
+ switch ((MatcherToken) token)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_TOKEN_AND:
|
|
|
14ad8e |
+ case MATCHER_TOKEN_OR:
|
|
|
14ad8e |
+ case MATCHER_TOKEN_NOT:
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ MetaWindowMatcher *a, *b;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ a = meta_window_matcher_from_scanner (scanner, error);
|
|
|
14ad8e |
+ if (!a)
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if ((MatcherToken) token != MATCHER_TOKEN_NOT)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ b = meta_window_matcher_from_scanner (scanner, error);
|
|
|
14ad8e |
+ if (!b)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ meta_window_matcher_free (a);
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ switch ((MatcherToken) token)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_TOKEN_AND:
|
|
|
14ad8e |
+ matcher = meta_window_matcher_new_and (a, b);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_TOKEN_OR:
|
|
|
14ad8e |
+ matcher = meta_window_matcher_new_or (a, b);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_TOKEN_NOT:
|
|
|
14ad8e |
+ matcher = meta_window_matcher_new_not (a);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ default:
|
|
|
14ad8e |
+ g_assert_not_reached();
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_TOKEN_EQ:
|
|
|
14ad8e |
+ case MATCHER_TOKEN_GLOB:
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ MatcherOperand operand;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ switch ((MatcherToken) g_scanner_get_next_token (scanner))
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_TOKEN_NAME:
|
|
|
14ad8e |
+ operand = MATCHER_OPERAND_NAME;
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_TOKEN_CLASS:
|
|
|
14ad8e |
+ operand = MATCHER_OPERAND_CLASS;
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ default:
|
|
|
14ad8e |
+ set_error (scanner, error, "expected name/class");
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if (g_scanner_get_next_token (scanner) != G_TOKEN_STRING)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ set_error (scanner, error, "expected string");
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ value = g_scanner_cur_value (scanner);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ switch ((MatcherToken) token)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_TOKEN_EQ:
|
|
|
14ad8e |
+ matcher = meta_window_matcher_new_eq (operand, value.v_string);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_TOKEN_GLOB:
|
|
|
14ad8e |
+ matcher = meta_window_matcher_new_glob (operand, value.v_string);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ default:
|
|
|
14ad8e |
+ g_assert_not_reached();
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ default:
|
|
|
14ad8e |
+ set_error (scanner, error, "expected and/or/not/eq/glob");
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if (g_scanner_get_next_token (scanner) != G_TOKEN_RIGHT_PAREN)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ set_error (scanner, error, "expected ')'");
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return matcher;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+GSList *
|
|
|
14ad8e |
+meta_window_matcher_list_from_string (const char *str,
|
|
|
14ad8e |
+ GError **error)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ GScanner *scanner = g_scanner_new (&scanner_config);
|
|
|
14ad8e |
+ GSList *result = NULL;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "and", GINT_TO_POINTER (MATCHER_TOKEN_AND));
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "or", GINT_TO_POINTER (MATCHER_TOKEN_OR));
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "not", GINT_TO_POINTER (MATCHER_TOKEN_NOT));
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "eq", GINT_TO_POINTER (MATCHER_TOKEN_EQ));
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "glob", GINT_TO_POINTER (MATCHER_TOKEN_GLOB));
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "name", GINT_TO_POINTER (MATCHER_TOKEN_NAME));
|
|
|
14ad8e |
+ g_scanner_scope_add_symbol (scanner, 0, "class", GINT_TO_POINTER (MATCHER_TOKEN_CLASS));
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_scanner_input_text (scanner, str, strlen (str));
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ while (g_scanner_peek_next_token (scanner) != G_TOKEN_EOF)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher = meta_window_matcher_from_scanner (scanner, error);
|
|
|
14ad8e |
+ if (!matcher)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ meta_window_matcher_list_free (result);
|
|
|
14ad8e |
+ return NULL;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ result = g_slist_prepend (result, matcher);
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_scanner_destroy (scanner);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return g_slist_reverse (result);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+#ifdef BUILD_MATCHER_TESTS
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+append_operand_to_string (GString *string,
|
|
|
14ad8e |
+ MatcherOperand operand)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ if (operand == MATCHER_OPERAND_NAME)
|
|
|
14ad8e |
+ g_string_append (string, "name");
|
|
|
14ad8e |
+ else
|
|
|
14ad8e |
+ g_string_append (string, "class");
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+append_string_to_string (GString *str,
|
|
|
14ad8e |
+ const char *to_append)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ const char *p;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_string_append_c (str, '"');
|
|
|
14ad8e |
+ for (p = to_append; *p; p++)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ if (*p == '"')
|
|
|
14ad8e |
+ g_string_append (str, "\\\"");
|
|
|
14ad8e |
+ else
|
|
|
14ad8e |
+ g_string_append_c (str, *p);
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+ g_string_append_c (str, '"');
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+append_matcher_to_string (GString *str,
|
|
|
14ad8e |
+ MetaWindowMatcher *matcher)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ switch (matcher->type)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ case MATCHER_AND:
|
|
|
14ad8e |
+ g_string_append (str, "(and ");
|
|
|
14ad8e |
+ append_matcher_to_string (str, matcher->u.and.a);
|
|
|
14ad8e |
+ g_string_append_c (str, ' ');
|
|
|
14ad8e |
+ append_matcher_to_string (str, matcher->u.and.b);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_OR:
|
|
|
14ad8e |
+ g_string_append (str, "(or ");
|
|
|
14ad8e |
+ append_matcher_to_string (str, matcher->u.or.a);
|
|
|
14ad8e |
+ g_string_append_c (str, ' ');
|
|
|
14ad8e |
+ append_matcher_to_string (str, matcher->u.or.b);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_NOT:
|
|
|
14ad8e |
+ g_string_append (str, "(not ");
|
|
|
14ad8e |
+ append_matcher_to_string (str, matcher->u.not.a);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_EQ:
|
|
|
14ad8e |
+ g_string_append (str, "(eq ");
|
|
|
14ad8e |
+ append_operand_to_string (str, matcher->u.eq.operand);
|
|
|
14ad8e |
+ g_string_append_c (str, ' ');
|
|
|
14ad8e |
+ append_string_to_string (str, matcher->u.eq.str);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ case MATCHER_GLOB:
|
|
|
14ad8e |
+ g_string_append (str, "(glob ");
|
|
|
14ad8e |
+ append_operand_to_string (str, matcher->u.glob.operand);
|
|
|
14ad8e |
+ g_string_append_c (str, ' ');
|
|
|
14ad8e |
+ append_string_to_string (str, matcher->u.glob.str);
|
|
|
14ad8e |
+ break;
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_string_append_c (str, ')');
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static char *
|
|
|
14ad8e |
+meta_window_matcher_list_to_string (GSList *list)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ GSList *l;
|
|
|
14ad8e |
+ GString *str = g_string_new (NULL);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ for (l = list; l; l = l->next)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ if (str->len > 0)
|
|
|
14ad8e |
+ g_string_append_c (str, ' ');
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ append_matcher_to_string (str, l->data);
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return g_string_free (str, FALSE);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+test_roundtrip (const char *str)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ GError *error = NULL;
|
|
|
14ad8e |
+ GSList *list = meta_window_matcher_list_from_string (str, &error);
|
|
|
14ad8e |
+ char *result;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if (error != NULL)
|
|
|
14ad8e |
+ g_error ("Failed to parse '%s': %s\n", str, error->message);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ result = meta_window_matcher_list_to_string (list);
|
|
|
14ad8e |
+ if (strcmp (result, str) != 0)
|
|
|
14ad8e |
+ g_error ("Round-trip conversion of '%s' gave '%s'\n", str, result);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ g_free (result);
|
|
|
14ad8e |
+ meta_window_matcher_list_free (list);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+static void
|
|
|
14ad8e |
+test_matches (const char *str,
|
|
|
14ad8e |
+ const char *window_name,
|
|
|
14ad8e |
+ const char *window_class,
|
|
|
14ad8e |
+ gboolean expected)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ GError *error = NULL;
|
|
|
14ad8e |
+ GSList *list = meta_window_matcher_list_from_string (str, &error);
|
|
|
14ad8e |
+ gboolean matches;
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ if (error != NULL)
|
|
|
14ad8e |
+ g_error ("Failed to parse '%s': %s\n", str, error->message);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ matches = meta_window_matcher_list_matches (list, window_name, window_class))
|
|
|
14ad8e |
+ if (matches != expected)
|
|
|
14ad8e |
+ {
|
|
|
14ad8e |
+ g_error ("Tested '%s' against name=%s, class=%s, expected %s, got %s\n",
|
|
|
14ad8e |
+ str, window_name, window_class,
|
|
|
14ad8e |
+ expected ? "true" : "false",
|
|
|
14ad8e |
+ matches ? "true" : "false");
|
|
|
14ad8e |
+ }
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ meta_window_matcher_list_free (list);
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+int main (int argc, char **argv)
|
|
|
14ad8e |
+{
|
|
|
14ad8e |
+ test_roundtrip ("(eq name \"foo\")");
|
|
|
14ad8e |
+ test_roundtrip ("(eq name \"fo\\\"o\")");
|
|
|
14ad8e |
+ test_roundtrip ("(glob class \"*bar?baz\")");
|
|
|
14ad8e |
+ test_roundtrip ("(and (eq name \"foo\") (glob class \"*bar?baz\"))");
|
|
|
14ad8e |
+ test_roundtrip ("(or (eq name \"foo\") (glob class \"*bar?baz\"))");
|
|
|
14ad8e |
+ test_roundtrip ("(not (eq name \"foo\"))");
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ test_roundtrip ("(eq name \"foo\") (glob class \"*bar?baz\")");
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ test_matches ("(eq name 'foo')", "foo", NULL, TRUE);
|
|
|
14ad8e |
+ test_matches ("(eq name 'foo')", "foob", NULL, FALSE);
|
|
|
14ad8e |
+ test_matches ("(eq name 'foo')", NULL, NULL, FALSE);
|
|
|
14ad8e |
+ test_matches ("(eq class 'bar')", "foo", "bar", TRUE);
|
|
|
14ad8e |
+ test_matches ("(eq class 'bar')", NULL, NULL, FALSE);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ test_matches ("(glob name 'foo*')", "foooo", NULL, TRUE);
|
|
|
14ad8e |
+ test_matches ("(glob name 'foo*')", NULL, NULL, FALSE);
|
|
|
14ad8e |
+ test_matches ("(glob class 'b*r')", "foooo", "baaaar", TRUE);
|
|
|
14ad8e |
+ test_matches ("(glob class 'b*r')", NULL, NULL, FALSE);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ test_matches ("(and (eq name 'foo') (eq class 'bar'))", "foo", "bar", TRUE);
|
|
|
14ad8e |
+ test_matches ("(and (eq name 'foo') (eq class 'bar'))", "foo", "baz", FALSE);
|
|
|
14ad8e |
+ test_matches ("(and (eq name 'foo') (not (eq class 'bar')))", "foo", "bar", FALSE);
|
|
|
14ad8e |
+ test_matches ("(and (eq name 'foo') (not (eq class 'bar')))", "foo", "baz", TRUE);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ test_matches ("(or (eq name 'foo') (eq class 'bar'))", "foo", "baz", TRUE);
|
|
|
14ad8e |
+ test_matches ("(or (eq name 'foo') (eq class 'bar'))", "fof", "baz", FALSE);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ return 0;
|
|
|
14ad8e |
+}
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+#endif /* BUILD_MATCHER_TESTS */
|
|
|
14ad8e |
diff --git a/src/core/window-matcher.h b/src/core/window-matcher.h
|
|
|
14ad8e |
new file mode 100644
|
|
|
14ad8e |
index 0000000..7fc7826
|
|
|
14ad8e |
--- /dev/null
|
|
|
14ad8e |
+++ b/src/core/window-matcher.h
|
|
|
14ad8e |
@@ -0,0 +1,46 @@
|
|
|
14ad8e |
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+/* Tiny language for matching against windows
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * Expression Syntax:
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * (and <expr> <expr>)
|
|
|
14ad8e |
+ * (or <expr> <expr>)
|
|
|
14ad8e |
+ * (not <expr>)
|
|
|
14ad8e |
+ * (eq [name|class] "<value>")
|
|
|
14ad8e |
+ * (glob [name|class] "<glob>")
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * A "matcher list" is a whitespace-separated list of expressions that are
|
|
|
14ad8e |
+ * implicitly or'ed together. Globs are shell style patterns with
|
|
|
14ad8e |
+ * matching 0 or more characters and ? matching one character.
|
|
|
14ad8e |
+ */
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+/*
|
|
|
14ad8e |
+ * Copyright (C) 2009 Red Hat, Inc.
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * This program is free software; you can redistribute it and/or
|
|
|
14ad8e |
+ * modify it under the terms of the GNU General Public License as
|
|
|
14ad8e |
+ * published by the Free Software Foundation; either version 2 of the
|
|
|
14ad8e |
+ * License, or (at your option) any later version.
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * This program is distributed in the hope that it will be useful, but
|
|
|
14ad8e |
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
14ad8e |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
14ad8e |
+ * General Public License for more details.
|
|
|
14ad8e |
+ *
|
|
|
14ad8e |
+ * You should have received a copy of the GNU General Public License
|
|
|
14ad8e |
+ * along with this program; if not, write to the Free Software
|
|
|
14ad8e |
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
14ad8e |
+ * 02111-1307, USA.
|
|
|
14ad8e |
+ */
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+#ifndef META_WINDOW_MATCHER_H
|
|
|
14ad8e |
+#define META_WINDOW_MATCHER_H
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+GSList * meta_window_matcher_list_from_string (const char *str,
|
|
|
14ad8e |
+ GError **error);
|
|
|
14ad8e |
+void meta_window_matcher_list_free (GSList *list);
|
|
|
14ad8e |
+gboolean meta_window_matcher_list_matches (GSList *list,
|
|
|
14ad8e |
+ const char *window_name,
|
|
|
14ad8e |
+ const char *window_class);
|
|
|
14ad8e |
+#endif /* META_WINDOW_MATCHER_H */
|
|
|
14ad8e |
diff --git a/src/core/window.c b/src/core/window.c
|
|
|
14ad8e |
index 2f2f800..5440160 100644
|
|
|
14ad8e |
--- a/src/core/window.c
|
|
|
14ad8e |
+++ b/src/core/window.c
|
|
|
14ad8e |
@@ -1981,7 +1981,14 @@ window_state_on_map (MetaWindow *window,
|
|
|
14ad8e |
{
|
|
|
14ad8e |
gboolean intervening_events;
|
|
|
14ad8e |
|
|
|
14ad8e |
- intervening_events = intervening_user_event_occurred (window);
|
|
|
14ad8e |
+ /* A 'no focus' window is a window that has been configured in GConf
|
|
|
14ad8e |
+ * to never take focus on map; typically it will be a notification
|
|
|
14ad8e |
+ * window from a legacy app that doesn't support _NET_WM_USER_TIME.
|
|
|
14ad8e |
+ */
|
|
|
14ad8e |
+ if (meta_prefs_window_is_no_focus (window->title, window->res_class))
|
|
|
14ad8e |
+ intervening_events = TRUE;
|
|
|
14ad8e |
+ else
|
|
|
14ad8e |
+ intervening_events = intervening_user_event_occurred (window);
|
|
|
14ad8e |
|
|
|
14ad8e |
*takes_focus = !intervening_events;
|
|
|
14ad8e |
*places_on_top = *takes_focus;
|
|
|
14ad8e |
diff --git a/src/include/prefs.h b/src/include/prefs.h
|
|
|
14ad8e |
index 673cb36..b86843c 100644
|
|
|
14ad8e |
--- a/src/include/prefs.h
|
|
|
14ad8e |
+++ b/src/include/prefs.h
|
|
|
14ad8e |
@@ -60,7 +60,8 @@ typedef enum
|
|
|
14ad8e |
META_PREF_CURSOR_SIZE,
|
|
|
14ad8e |
META_PREF_COMPOSITING_MANAGER,
|
|
|
14ad8e |
META_PREF_RESIZE_WITH_RIGHT_BUTTON,
|
|
|
14ad8e |
- META_PREF_FORCE_FULLSCREEN
|
|
|
14ad8e |
+ META_PREF_FORCE_FULLSCREEN,
|
|
|
14ad8e |
+ META_PREF_NO_FOCUS_WINDOWS
|
|
|
14ad8e |
} MetaPreference;
|
|
|
14ad8e |
|
|
|
14ad8e |
typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
|
|
|
14ad8e |
@@ -105,6 +106,9 @@ GDesktopTitlebarAction meta_prefs_get_action_double_click_titlebar (void);
|
|
|
14ad8e |
GDesktopTitlebarAction meta_prefs_get_action_middle_click_titlebar (void);
|
|
|
14ad8e |
GDesktopTitlebarAction meta_prefs_get_action_right_click_titlebar (void);
|
|
|
14ad8e |
|
|
|
14ad8e |
+gboolean meta_prefs_window_is_no_focus (const char *window_name,
|
|
|
14ad8e |
+ const char *window_class);
|
|
|
14ad8e |
+
|
|
|
14ad8e |
void meta_prefs_set_num_workspaces (int n_workspaces);
|
|
|
14ad8e |
|
|
|
14ad8e |
const char* meta_prefs_get_workspace_name (int i);
|
|
|
14ad8e |
diff --git a/src/metacity-schemas.convert b/src/metacity-schemas.convert
|
|
|
14ad8e |
index 46f3104..9c271c6 100644
|
|
|
14ad8e |
--- a/src/metacity-schemas.convert
|
|
|
14ad8e |
+++ b/src/metacity-schemas.convert
|
|
|
14ad8e |
@@ -1,3 +1,4 @@
|
|
|
14ad8e |
[org.gnome.metacity]
|
|
|
14ad8e |
compositing-manager = /apps/metacity/general/compositing_manager
|
|
|
14ad8e |
reduced-resources = /apps/metacity/general/reduced_resources
|
|
|
14ad8e |
+no-focus-windows = /apps/metacity/general/no_focus_windows
|
|
|
14ad8e |
diff --git a/src/org.gnome.metacity.gschema.xml.in b/src/org.gnome.metacity.gschema.xml.in
|
|
|
14ad8e |
index 8fcdd7c..6900fa6 100644
|
|
|
14ad8e |
--- a/src/org.gnome.metacity.gschema.xml.in
|
|
|
14ad8e |
+++ b/src/org.gnome.metacity.gschema.xml.in
|
|
|
14ad8e |
@@ -22,6 +22,27 @@
|
|
|
14ad8e |
However, the wireframe feature is disabled when accessibility is on.
|
|
|
14ad8e |
</_description>
|
|
|
14ad8e |
</key>
|
|
|
14ad8e |
+ <key name="no-focus-windows" type="s">
|
|
|
14ad8e |
+ <default>''</default>
|
|
|
14ad8e |
+ <_summary>New windows that shouldn't get focus</_summary>
|
|
|
14ad8e |
+ <_description>
|
|
|
14ad8e |
+ This option provides a way to specify new windows that shouldn't get
|
|
|
14ad8e |
+ focus. Normally an application specifies whether or not it gets focus
|
|
|
14ad8e |
+ by setting the _NET_WM_USER_TIME property, but legacy applications
|
|
|
14ad8e |
+ may not set this, which can cause unwanted focus stealing.
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ The contents of this property is a space-separated list of expressions
|
|
|
14ad8e |
+ to match against windows. If any of the expressions match a window
|
|
|
14ad8e |
+ then the window will not get focus. The syntax of expressions is:
|
|
|
14ad8e |
+
|
|
|
14ad8e |
+ (eq [name|class] "<value>"): window name (title) or the class from
|
|
|
14ad8e |
+ WM_CLASS matches <value> exactly.
|
|
|
14ad8e |
+ (glob [name|class] "<glob>"): window name (title) or the class from
|
|
|
14ad8e |
+ WM_CLASS matches the shell-style glob pattern <glob>.
|
|
|
14ad8e |
+ (and <expr> <expr>) (or <expr> <expr>) (not <expr): Boolean combinations
|
|
|
14ad8e |
+ of expressions.
|
|
|
14ad8e |
+ </_description>
|
|
|
14ad8e |
+ </key>
|
|
|
14ad8e |
</schema>
|
|
|
14ad8e |
|
|
|
14ad8e |
</schemalist>
|
|
|
14ad8e |
--
|
|
|
14ad8e |
1.7.9
|
|
|
14ad8e |
|