Skip to content

Commit c7aa7f6

Browse files
committed
Implemented xdg-activation logic
1 parent c5067e6 commit c7aa7f6

File tree

14 files changed

+428
-7
lines changed

14 files changed

+428
-7
lines changed

.clang-format

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
UseTab: Always
2+
IndentWidth: 4
3+
TabWidth: 4
4+
ColumnLimit: 80
5+
AllowShortFunctionsOnASingleLine: None

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ SwayNotificationCenter
77
./swaync*
88
./src/swaync
99
./src/swaync*
10+
.cache/
1011
swaync-git*

meson.build

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@ add_project_arguments(['-Wno-error=int-conversion'], language: 'c')
88
add_project_arguments(['--enable-gobject-tracing'], language: 'vala')
99
add_project_arguments(['--enable-checking'], language: 'vala')
1010

11-
i18n = import('i18n')
11+
cc = meson.get_compiler('c')
12+
vala = meson.get_compiler('vala')
13+
1214
gnome = import('gnome')
1315

1416
app_resources = []
1517

1618
config_path = join_paths(get_option('sysconfdir'), 'xdg', 'swaync')
1719

20+
# Wayland protocols
21+
protocol_dep = []
22+
subdir('protocols')
23+
1824
subdir('data')
1925
subdir('src')
2026

protocols/meson.build

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
dep_scanner = dependency('wayland-scanner', native: true)
2+
prog_scanner = find_program(dep_scanner.get_variable(pkgconfig: 'wayland_scanner'))
3+
4+
protocol_file = files(
5+
'xdg-activation-v1.xml',
6+
)
7+
8+
protocol_sources = []
9+
protocol_sources += custom_target(
10+
'xdg-activation-v1-client-protocol.h',
11+
command: [prog_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
12+
input: protocol_file,
13+
output: 'xdg-activation-v1-client-protocol.h',
14+
)
15+
16+
output_type = 'private-code'
17+
if dep_scanner.version().version_compare('< 1.14.91')
18+
output_type = 'code'
19+
endif
20+
protocol_sources += custom_target(
21+
'xdg-activation-protocol.c',
22+
command: [prog_scanner, output_type, '@INPUT@', '@OUTPUT@'],
23+
input: protocol_file,
24+
output: 'xdg-activation-protocol.c',
25+
)
26+
27+
protocol_dep += declare_dependency(
28+
dependencies: [
29+
vala.find_library('xdg-activation-v1', dirs: meson.current_source_dir()),
30+
dependency('wayland-client'),
31+
],
32+
include_directories: include_directories('.'),
33+
sources: protocol_sources,
34+
)

protocols/xdg-activation-v1.deps

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
wayland-client

protocols/xdg-activation-v1.vapi

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// vim: ft=vala
2+
3+
namespace XDG.Activation {
4+
[CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "struct xdg_activation_v1", cprefix = "xdg_activation_v1_")]
5+
public class Activation : Wl.Proxy {
6+
[CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "xdg_activation_v1_interface")]
7+
public static Wl.Interface iface;
8+
public void set_user_data (void * user_data);
9+
public void * get_user_data ();
10+
public uint32 get_version ();
11+
12+
public void destroy ();
13+
14+
public Token * get_activation_token ();
15+
public void activate (string token, Wl.Surface surface);
16+
}
17+
18+
[CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "enum xdg_activation_token_v1_error", cprefix = "XDG_ACTIVATION_TOKEN_V1_ERROR_", has_type_id = false)]
19+
public enum error {
20+
ALREADY_USED = 0,
21+
}
22+
23+
[CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "struct xdg_activation_token_v1", cprefix = "xdg_activation_token_v1_")]
24+
public class Token : Wl.Proxy {
25+
[CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "xdg_activation_token_v1_listener")]
26+
public static Wl.Interface iface;
27+
public void set_user_data (void * user_data);
28+
public void * get_user_data ();
29+
public uint32 get_version ();
30+
public void destroy ();
31+
32+
public int add_listener (TokenListener listener, void * data);
33+
34+
public void set_serial (uint32 serial, Wl.Seat seat);
35+
public void set_app_id (string app_id);
36+
public void set_surface (Wl.Surface surface);
37+
public void commit ();
38+
}
39+
40+
[CCode (cname = "struct xdg_activation_token_v1_listener", has_type_id = false)]
41+
public struct TokenListener {
42+
public TokenListenerDone done;
43+
}
44+
45+
[CCode (has_target = false, has_typedef = false)]
46+
public delegate void TokenListenerDone (void * data, Token activation_token, string token);
47+
}
48+

protocols/xdg-activation-v1.xml

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<protocol name="xdg_activation_v1">
3+
4+
<copyright>
5+
Copyright © 2020 Aleix Pol Gonzalez &lt;[email protected]&gt;
6+
Copyright © 2020 Carlos Garnacho &lt;[email protected]&gt;
7+
8+
Permission is hereby granted, free of charge, to any person obtaining a
9+
copy of this software and associated documentation files (the "Software"),
10+
to deal in the Software without restriction, including without limitation
11+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
12+
and/or sell copies of the Software, and to permit persons to whom the
13+
Software is furnished to do so, subject to the following conditions:
14+
15+
The above copyright notice and this permission notice (including the next
16+
paragraph) shall be included in all copies or substantial portions of the
17+
Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25+
DEALINGS IN THE SOFTWARE.
26+
</copyright>
27+
28+
<description summary="Protocol for requesting activation of surfaces">
29+
The way for a client to pass focus to another toplevel is as follows.
30+
31+
The client that intends to activate another toplevel uses the
32+
xdg_activation_v1.get_activation_token request to get an activation token.
33+
This token is then forwarded to the client, which is supposed to activate
34+
one of its surfaces, through a separate band of communication.
35+
36+
One established way of doing this is through the XDG_ACTIVATION_TOKEN
37+
environment variable of a newly launched child process. The child process
38+
should unset the environment variable again right after reading it out in
39+
order to avoid propagating it to other child processes.
40+
41+
Another established way exists for Applications implementing the D-Bus
42+
interface org.freedesktop.Application, which should get their token under
43+
activation-token on their platform_data.
44+
45+
In general activation tokens may be transferred across clients through
46+
means not described in this protocol.
47+
48+
The client to be activated will then pass the token
49+
it received to the xdg_activation_v1.activate request. The compositor can
50+
then use this token to decide how to react to the activation request.
51+
52+
The token the activating client gets may be ineffective either already at
53+
the time it receives it, for example if it was not focused, for focus
54+
stealing prevention. The activating client will have no way to discover
55+
the validity of the token, and may still forward it to the to be activated
56+
client.
57+
58+
The created activation token may optionally get information attached to it
59+
that can be used by the compositor to identify the application that we
60+
intend to activate. This can for example be used to display a visual hint
61+
about what application is being started.
62+
63+
Warning! The protocol described in this file is currently in the testing
64+
phase. Backward compatible changes may be added together with the
65+
corresponding interface version bump. Backward incompatible changes can
66+
only be done by creating a new major version of the extension.
67+
</description>
68+
69+
<interface name="xdg_activation_v1" version="1">
70+
<description summary="interface for activating surfaces">
71+
A global interface used for informing the compositor about applications
72+
being activated or started, or for applications to request to be
73+
activated.
74+
</description>
75+
76+
<request name="destroy" type="destructor">
77+
<description summary="destroy the xdg_activation object">
78+
Notify the compositor that the xdg_activation object will no longer be
79+
used.
80+
81+
The child objects created via this interface are unaffected and should
82+
be destroyed separately.
83+
</description>
84+
</request>
85+
86+
<request name="get_activation_token">
87+
<description summary="requests a token">
88+
Creates an xdg_activation_token_v1 object that will provide
89+
the initiating client with a unique token for this activation. This
90+
token should be offered to the clients to be activated.
91+
</description>
92+
93+
<arg name="id" type="new_id" interface="xdg_activation_token_v1"/>
94+
</request>
95+
96+
<request name="activate">
97+
<description summary="notify new interaction being available">
98+
Requests surface activation. It's up to the compositor to display
99+
this information as desired, for example by placing the surface above
100+
the rest.
101+
102+
The compositor may know who requested this by checking the activation
103+
token and might decide not to follow through with the activation if it's
104+
considered unwanted.
105+
106+
Compositors can ignore unknown activation tokens when an invalid
107+
token is passed.
108+
</description>
109+
<arg name="token" type="string" summary="the activation token of the initiating client"/>
110+
<arg name="surface" type="object" interface="wl_surface"
111+
summary="the wl_surface to activate"/>
112+
</request>
113+
</interface>
114+
115+
<interface name="xdg_activation_token_v1" version="1">
116+
<description summary="an exported activation handle">
117+
An object for setting up a token and receiving a token handle that can
118+
be passed as an activation token to another client.
119+
120+
The object is created using the xdg_activation_v1.get_activation_token
121+
request. This object should then be populated with the app_id, surface
122+
and serial information and committed. The compositor shall then issue a
123+
done event with the token. In case the request's parameters are invalid,
124+
the compositor will provide an invalid token.
125+
</description>
126+
127+
<enum name="error">
128+
<entry name="already_used" value="0"
129+
summary="The token has already been used previously"/>
130+
</enum>
131+
132+
<request name="set_serial">
133+
<description summary="specifies the seat and serial of the activating event">
134+
Provides information about the seat and serial event that requested the
135+
token.
136+
137+
The serial can come from an input or focus event. For instance, if a
138+
click triggers the launch of a third-party client, the launcher client
139+
should send a set_serial request with the serial and seat from the
140+
wl_pointer.button event.
141+
142+
Some compositors might refuse to activate toplevels when the token
143+
doesn't have a valid and recent enough event serial.
144+
145+
Must be sent before commit. This information is optional.
146+
</description>
147+
<arg name="serial" type="uint"
148+
summary="the serial of the event that triggered the activation"/>
149+
<arg name="seat" type="object" interface="wl_seat"
150+
summary="the wl_seat of the event"/>
151+
</request>
152+
153+
<request name="set_app_id">
154+
<description summary="specifies the application being activated">
155+
The requesting client can specify an app_id to associate the token
156+
being created with it.
157+
158+
Must be sent before commit. This information is optional.
159+
</description>
160+
<arg name="app_id" type="string"
161+
summary="the application id of the client being activated."/>
162+
</request>
163+
164+
<request name="set_surface">
165+
<description summary="specifies the surface requesting activation">
166+
This request sets the surface requesting the activation. Note, this is
167+
different from the surface that will be activated.
168+
169+
Some compositors might refuse to activate toplevels when the token
170+
doesn't have a requesting surface.
171+
172+
Must be sent before commit. This information is optional.
173+
</description>
174+
<arg name="surface" type="object" interface="wl_surface"
175+
summary="the requesting surface"/>
176+
</request>
177+
178+
<request name="commit">
179+
<description summary="issues the token request">
180+
Requests an activation token based on the different parameters that
181+
have been offered through set_serial, set_surface and set_app_id.
182+
</description>
183+
</request>
184+
185+
<event name="done">
186+
<description summary="the exported activation token">
187+
The 'done' event contains the unique token of this activation request
188+
and notifies that the provider is done.
189+
</description>
190+
<arg name="token" type="string" summary="the exported activation token"/>
191+
</event>
192+
193+
<request name="destroy" type="destructor">
194+
<description summary="destroy the xdg_activation_token_v1 object">
195+
Notify the compositor that the xdg_activation_token_v1 object will no
196+
longer be used. The received token stays valid.
197+
</description>
198+
</request>
199+
</interface>
200+
</protocol>

src/WaylandUtils/WaylandUtils.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <gdk/gdk.h>
2+
#include <gdk/gdkwayland.h>
3+
#include <glib.h>
4+
#include <stdio.h>
5+
#include <wayland-client.h>
6+
7+
/** GDK doesn't provide a vapi file for GDK Wayland... */
8+
9+
#define PRINT_ERROR \
10+
g_error("Gdk Display isn't a Wayland display! Only Wayland is supported")
11+
12+
struct wl_display *get_wl_display() {
13+
GdkDisplay *display = gdk_display_get_default();
14+
if (GDK_IS_WAYLAND_DISPLAY(display)) {
15+
return gdk_wayland_display_get_wl_display(display);
16+
}
17+
PRINT_ERROR;
18+
return NULL;
19+
}
20+
21+
struct wl_surface *get_wl_surface(GdkWindow *window) {
22+
if (GDK_IS_WAYLAND_WINDOW(window)) {
23+
return gdk_wayland_window_get_wl_surface(window);
24+
}
25+
PRINT_ERROR;
26+
return NULL;
27+
}

src/WaylandUtils/WaylandUtils.vala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
extern Wl.Display * get_wl_display ();
2+
extern Wl.Surface * get_wl_surface (Gdk.Window window);

src/meson.build

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,13 @@ app_sources = [
6464
widget_sources,
6565
'blankWindow/blankWindow.vala',
6666
'functions.vala',
67+
'WaylandUtils/WaylandUtils.c',
68+
'WaylandUtils/WaylandUtils.vala',
69+
'xdg_activation.vala',
6770
constants,
6871
]
6972

70-
assert(meson.get_compiler('vala').version() >= '0.56')
73+
assert(vala.version() >= '0.56')
7174

7275
app_deps = [
7376
dependency('gio-2.0', version: '>= 2.50'),
@@ -80,9 +83,11 @@ app_deps = [
8083
fallback: ['gtk-layer-shell-0', 'gtk-layer-shell'],
8184
version: '>= 0.8.0'
8285
),
83-
meson.get_compiler('c').find_library('m', required : true),
84-
meson.get_compiler('vala').find_library('posix'),
86+
cc.find_library('m', required : true),
87+
vala.find_library('posix'),
8588
dependency('gee-0.8'),
89+
dependency('wayland-client'),
90+
protocol_dep,
8691
]
8792

8893
# Checks if the user wants scripting enabled

0 commit comments

Comments
 (0)