├── LICENSE
├── README.md
├── ipc-scripts
└── wf-info.py
├── meson.build
├── metadata
├── meson.build
└── wf-info.xml
├── proto
├── meson.build
└── wayfire-information.xml
└── src
├── client
├── meson.build
├── qt
│ ├── gui.py
│ └── wf_info_base.py
├── wf-info.cpp
└── wf-info.hpp
├── main.cpp
├── meson.build
└── plugin
├── ipc-rules-common.hpp
├── wayfire-information.cpp
└── wayfire-information.hpp
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2022 Scott Moreau
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wf-info
2 | A simple wayfire plugin and program to get information from wayfire.
3 |
4 | ## Build
5 |
6 | meson build --prefix=/usr
7 |
8 | ninja -C build
9 |
10 | sudo ninja -C build install
11 |
12 | ## Runtime
13 |
14 | Enable Information Protocol plugin
15 |
16 | Run `wf-info` and click on a window, run `wf-info -l` to list information about all windows, or use `wf-info -i $id` where `$id` is the ID of the view about which you want info. An ID of -1 means the focused view.
17 |
18 | ## Examples
19 |
20 | ```
21 | $ wf-info
22 | =========================
23 | View ID: 1112
24 | Client PID: 1562086
25 | Output: DP-2(ID: 1)
26 | Workspace: 0,0
27 | App ID: python3
28 | Title: Wayfire Window Information
29 | Role: TOPLEVEL
30 | Geometry: 710,231 500x629
31 | Xwayland: false
32 | Focused: false
33 | =========================
34 | ```
35 |
36 | `python3 src/client/qt/gui.py`
37 |
38 | 
39 |
--------------------------------------------------------------------------------
/ipc-scripts/wf-info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import json
4 | from wayfire import WayfireSocket
5 | from wayfire.extra.wpe import WPE
6 |
7 | sock = WayfireSocket()
8 | wpe = WPE(sock)
9 |
10 | print(f"View Info:\n{json.dumps(wpe.get_view_info(), indent=2, ensure_ascii=False)}")
11 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'wf-info',
3 | 'c',
4 | 'cpp',
5 | version: '0.7.0',
6 | license: 'MIT',
7 | meson_version: '>=0.51.0',
8 | default_options: [
9 | 'cpp_std=c++17',
10 | 'c_std=c11',
11 | ],
12 | )
13 |
14 | wayfire = dependency('wayfire')
15 |
16 | add_project_arguments(['-DWLR_USE_UNSTABLE'], language: ['cpp', 'c'])
17 |
18 | subdir('metadata')
19 | subdir('proto')
20 | subdir('src')
21 |
--------------------------------------------------------------------------------
/metadata/meson.build:
--------------------------------------------------------------------------------
1 | install_data('wf-info.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
--------------------------------------------------------------------------------
/metadata/wf-info.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <_short>Information Protocol
5 | <_long>Support for additional information about views and the desktop.
6 | Utility
7 |
8 |
9 |
--------------------------------------------------------------------------------
/proto/meson.build:
--------------------------------------------------------------------------------
1 | wayland_protos = dependency('wayland-protocols')
2 | wayland_server = dependency('wayland-server')
3 | wayland_client = dependency('wayland-client')
4 |
5 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
6 |
7 | wayland_scanner = find_program('wayland-scanner')
8 |
9 | wayland_scanner_server = generator(
10 | wayland_scanner,
11 | output: '@BASENAME@-server-protocol.h',
12 | arguments: ['server-header', '@INPUT@', '@OUTPUT@'],
13 | )
14 |
15 | wayland_scanner_client = generator(
16 | wayland_scanner,
17 | output: '@BASENAME@-client-protocol.h',
18 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
19 | )
20 |
21 | wayland_scanner_code = generator(
22 | wayland_scanner,
23 | output: '@BASENAME@-protocol.c',
24 | arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
25 | )
26 |
27 | protocols = [
28 | 'wayfire-information.xml'
29 | ]
30 |
31 | wl_server_protos_src = []
32 | wl_server_protos_headers = []
33 |
34 | foreach p : protocols
35 | xml = join_paths(p)
36 | wl_server_protos_headers += wayland_scanner_server.process(xml)
37 | wl_server_protos_src += wayland_scanner_code.process(xml)
38 | endforeach
39 |
40 | lib_wl_server_protos = static_library('wl_server_protos', wl_server_protos_src + wl_server_protos_headers,
41 | dependencies: [wayland_server]) # for the include directory
42 |
43 | wf_server_protos = declare_dependency(
44 | link_with: lib_wl_server_protos,
45 | sources: wl_server_protos_headers,
46 | )
47 |
48 | wl_client_protos_src = []
49 | wl_client_protos_headers = []
50 |
51 | foreach p : protocols
52 | xml = join_paths(p)
53 | wl_client_protos_headers += wayland_scanner_client.process(xml)
54 | wl_client_protos_src += wayland_scanner_code.process(xml)
55 | endforeach
56 |
57 | lib_wl_client_protos = static_library('wl_client_protos', wl_client_protos_src + wl_client_protos_headers,
58 | dependencies: [wayland_client]) # for the include directory
59 |
60 | wf_client_protos = declare_dependency(
61 | link_with: lib_wl_client_protos,
62 | sources: wl_client_protos_headers,
63 | )
64 |
--------------------------------------------------------------------------------
/proto/wayfire-information.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | The MIT License (MIT)
6 |
7 | Copyright (c) 2022 Scott Moreau
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in all
17 | copies or substantial portions of the 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 THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE.
26 |
27 |
28 |
29 |
30 | Interface that allows clients to get information from wayfire.
31 |
32 |
33 |
34 |
35 | Get information about the selected view.
36 |
37 |
38 |
39 |
40 |
41 | Get information about the view from id.
42 |
43 |
44 |
45 |
46 |
47 |
48 | Get information about all views.
49 |
50 |
51 |
52 |
53 |
54 | Provide client with information about a view.
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Notify client that the complete list of views has been sent.
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/src/client/meson.build:
--------------------------------------------------------------------------------
1 | executable('wf-info', ['wf-info.cpp'],
2 | dependencies: [wayland_client, wf_client_protos],
3 | install: true)
--------------------------------------------------------------------------------
/src/client/qt/gui.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QHBoxLayout, QVBoxLayout, QWidget
2 | from wf_info_base import WfInfoBase
3 | from pywayland.client import Display
4 | from PyQt5.QtCore import Qt
5 | import signal
6 | import sys
7 |
8 | class WfInfoApp(QMainWindow):
9 | def __init__(self, wf_info, clipboard):
10 | super().__init__()
11 | self.wf_info = wf_info
12 | self.clipboard = clipboard
13 |
14 | self.setWindowTitle("Wayfire Window Information")
15 | self.setGeometry(100, 100, 400, 500)
16 |
17 | self.central_widget = QWidget()
18 | self.setCentralWidget(self.central_widget)
19 |
20 | self.info_button = QPushButton("Click to get window information")
21 |
22 | self.layout = QVBoxLayout()
23 |
24 | self.layout.addWidget(self.info_button)
25 |
26 | self.central_widget.setLayout(self.layout)
27 |
28 | self.info_button.clicked.connect(self.get_info_text)
29 | def reset_layout(self, layout):
30 | if layout is not None:
31 | while layout.count():
32 | item = layout.takeAt(0)
33 | widget = item.widget()
34 | if widget is not None:
35 | widget.setParent(None)
36 | else:
37 | self.reset_layout(item.layout())
38 | def reset(self):
39 | self.reset_layout(self.layout)
40 | self.layout.addWidget(self.info_button)
41 | def copy_text(self, text):
42 | self.clipboard.setText(text)
43 | def handle_view_info(self, ok, view_id, client_pid, workspace_x, workspace_y, app_id, title, role, x, y, width, height, is_xwayland, focused, output, output_id):
44 | self.reset()
45 | labels = ["LABEL", "view_id", "client_pid", "workspace_x", "workspace_y", "app_id", "title", "role", "x", "y", "width", "height", "is_xwayland", "focused", "output", "output_id"]
46 | values = ["VALUE", f"{view_id}", f"{client_pid}", f"{workspace_x}", f"{workspace_y}", f"{app_id}", f"{title}", f"{role}", f"{x}", f"{y}", f"{width}", f"{height}", f"{is_xwayland}", f"{focused}", f"{output}", f"{output_id}"]
47 | hlayout = QHBoxLayout()
48 | label_layout = QVBoxLayout()
49 | i = 0
50 | for label in labels:
51 | i = i + 1
52 | if i == 1:
53 | label_label = QLabel(label)
54 | else:
55 | label_label = QLabel(label + ": ")
56 | label_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
57 | label_layout.addWidget(label_label)
58 | hlayout.addLayout(label_layout)
59 | value_layout = QVBoxLayout()
60 | for value in values:
61 | value_label = QLabel(value)
62 | value_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
63 | value_layout.addWidget(value_label)
64 | hlayout.addLayout(value_layout)
65 | copy_button_layout = QVBoxLayout()
66 | i = 0
67 | for value in values:
68 | i = i + 1
69 | if i == 1:
70 | reset_button = QPushButton("Reset")
71 | reset_button.clicked.connect(self.reset)
72 | copy_button_layout.addWidget(reset_button)
73 | continue
74 | copy_button = QPushButton("Copy Value")
75 | copy_button.clicked.connect(lambda s=self, v=value: self.copy_text(v))
76 | copy_button_layout.addWidget(copy_button)
77 | hlayout.addLayout(copy_button_layout)
78 |
79 | self.layout.addLayout(hlayout)
80 | self.layout.addWidget(self.info_button)
81 | def get_info_text(self):
82 | self.wf_info["binding"].dispatcher["view_info"] = self.handle_view_info
83 | self.wf_info["binding"].view_info()
84 | self.wf_info["display"].dispatch(block=True)
85 |
86 |
87 | def handle_registry_global(wl_registry, id_num, iface_name, version):
88 | #print("global", id_num, iface_name)
89 |
90 | wf_info = wl_registry.user_data
91 | if iface_name == "wf_info_base":
92 | wf_info["enabled"] = True
93 | wf_info["binding"] = wl_registry.bind(id_num, WfInfoBase, 1)
94 | return 1
95 |
96 | def wf_info_create():
97 | wf_info = {}
98 |
99 | # Make the display and get the registry
100 | wf_info["display"] = Display()
101 | wf_info["display"].connect()
102 |
103 | wf_info["registry"] = wf_info["display"].get_registry()
104 | wf_info["registry"].user_data = wf_info
105 | wf_info["registry"].dispatcher["global"] = handle_registry_global
106 |
107 | wf_info["enabled"] = False
108 | wf_info["display"].roundtrip()
109 |
110 | if not wf_info["enabled"]:
111 | print("Wayfire information protocol not advertised by compositor. Is wf-info plugin enabled?", file=sys.stderr)
112 | wf_info["display"].disconnect()
113 | exit(-1)
114 |
115 | return wf_info
116 |
117 | def main():
118 | signal.signal(signal.SIGINT, signal.SIG_DFL)
119 |
120 | app = QApplication(sys.argv)
121 | clipboard = app.clipboard()
122 |
123 | wf_info = wf_info_create()
124 | window = WfInfoApp(wf_info, clipboard)
125 |
126 | window.show()
127 |
128 | ret = app.exec_()
129 | wf_info["display"].disconnect()
130 | sys.exit(ret)
131 |
132 | if __name__ == "__main__":
133 | main()
134 |
--------------------------------------------------------------------------------
/src/client/qt/wf_info_base.py:
--------------------------------------------------------------------------------
1 | # This file has been autogenerated by the pywayland scanner
2 |
3 | # The MIT License (MIT)
4 | #
5 | # Copyright (c) 2022 Scott Moreau
6 | #
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy
8 | # of this software and associated documentation files (the "Software"), to deal
9 | # in the Software without restriction, including without limitation the rights
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | # copies of the Software, and to permit persons to whom the Software is
12 | # furnished to do so, subject to the following conditions:
13 | #
14 | # The above copyright notice and this permission notice shall be included in all
15 | # copies or substantial portions of the Software.
16 | #
17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | # SOFTWARE.
24 |
25 | from __future__ import annotations
26 |
27 | from pywayland.protocol_core import (
28 | Argument,
29 | ArgumentType,
30 | Global,
31 | Interface,
32 | Proxy,
33 | Resource,
34 | )
35 |
36 |
37 | class WfInfoBase(Interface):
38 | """Wayfire desktop communication
39 |
40 | Interface that allows clients to get information from wayfire.
41 | """
42 |
43 | name = "wf_info_base"
44 | version = 1
45 |
46 |
47 | class WfInfoBaseProxy(Proxy[WfInfoBase]):
48 | interface = WfInfoBase
49 |
50 | @WfInfoBase.request()
51 | def view_info(self) -> None:
52 | """Get information about the selected view
53 |
54 | Get information about the selected view.
55 | """
56 | self._marshal(0)
57 |
58 | @WfInfoBase.request(
59 | Argument(ArgumentType.Int),
60 | )
61 | def view_info_id(self, view_id: int) -> None:
62 | """Get information about the view from id
63 |
64 | Get information about the view from id.
65 |
66 | :param view_id:
67 | view ID
68 | :type view_id:
69 | `ArgumentType.Int`
70 | """
71 | self._marshal(1, view_id)
72 |
73 | @WfInfoBase.request()
74 | def view_info_list(self) -> None:
75 | """Get information from all views
76 |
77 | Get information about all views.
78 | """
79 | self._marshal(2)
80 |
81 |
82 | class WfInfoBaseResource(Resource):
83 | interface = WfInfoBase
84 |
85 | @WfInfoBase.event(
86 | Argument(ArgumentType.Int),
87 | Argument(ArgumentType.Int),
88 | Argument(ArgumentType.Int),
89 | Argument(ArgumentType.Int),
90 | Argument(ArgumentType.String),
91 | Argument(ArgumentType.String),
92 | Argument(ArgumentType.String),
93 | Argument(ArgumentType.Int),
94 | Argument(ArgumentType.Int),
95 | Argument(ArgumentType.Int),
96 | Argument(ArgumentType.Int),
97 | Argument(ArgumentType.Int),
98 | Argument(ArgumentType.Int),
99 | Argument(ArgumentType.String),
100 | Argument(ArgumentType.Uint),
101 | )
102 | def view_info(self, view_id: int, client_pid: int, workspace_x: int, workspace_y: int, app_id: str, title: str, role: str, x: int, y: int, width: int, height: int, is_xwayland: int, focused: int, output: str, output_id: int) -> None:
103 | """Export information about a view to a client
104 |
105 | Provide client with information about a view.
106 |
107 | :param view_id:
108 | view wayfire ID
109 | :type view_id:
110 | `ArgumentType.Int`
111 | :param client_pid:
112 | client PID
113 | :type client_pid:
114 | `ArgumentType.Int`
115 | :param workspace_x:
116 | view workspace x
117 | :type workspace_x:
118 | `ArgumentType.Int`
119 | :param workspace_y:
120 | view workspace y
121 | :type workspace_y:
122 | `ArgumentType.Int`
123 | :param app_id:
124 | view application ID
125 | :type app_id:
126 | `ArgumentType.String`
127 | :param title:
128 | view title
129 | :type title:
130 | `ArgumentType.String`
131 | :param role:
132 | view role
133 | :type role:
134 | `ArgumentType.String`
135 | :param x:
136 | view x position
137 | :type x:
138 | `ArgumentType.Int`
139 | :param y:
140 | view y position
141 | :type y:
142 | `ArgumentType.Int`
143 | :param width:
144 | view width
145 | :type width:
146 | `ArgumentType.Int`
147 | :param height:
148 | view height
149 | :type height:
150 | `ArgumentType.Int`
151 | :param is_xwayland:
152 | whether view is xwayland
153 | :type is_xwayland:
154 | `ArgumentType.Int`
155 | :param focused:
156 | whether view is focused
157 | :type focused:
158 | `ArgumentType.Int`
159 | :param output:
160 | Name of the view's output
161 | :type output:
162 | `ArgumentType.String`
163 | :param output_id:
164 | ID of the view's output
165 | :type output_id:
166 | `ArgumentType.Uint`
167 | """
168 | self._post_event(0, view_id, client_pid, workspace_x, workspace_y, app_id, title, role, x, y, width, height, is_xwayland, focused, output, output_id)
169 |
170 | @WfInfoBase.event()
171 | def done(self) -> None:
172 | """Notify client that the complete list of views has been sent
173 |
174 | Notify client that the complete list of views has been sent.
175 | """
176 | self._post_event(1)
177 |
178 |
179 | class WfInfoBaseGlobal(Global):
180 | interface = WfInfoBase
181 |
182 |
183 | WfInfoBase._gen_c()
184 | WfInfoBase.proxy_class = WfInfoBaseProxy
185 | WfInfoBase.resource_class = WfInfoBaseResource
186 | WfInfoBase.global_class = WfInfoBaseGlobal
187 |
--------------------------------------------------------------------------------
/src/client/wf-info.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2022 Scott Moreau
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #include "wf-info.hpp"
32 |
33 | static void registry_add(void *data, struct wl_registry *registry,
34 | uint32_t id, const char *interface,
35 | uint32_t version)
36 | {
37 | WfInfo *wfm = (WfInfo *) data;
38 |
39 | if (strcmp(interface, wf_info_base_interface.name) == 0)
40 | {
41 | wfm->wf_information_manager = (wf_info_base *)
42 | wl_registry_bind(registry, id,
43 | &wf_info_base_interface, 1);
44 | }
45 | }
46 |
47 | static void registry_remove(void *data, struct wl_registry *registry,
48 | uint32_t id)
49 | {
50 | exit(0);
51 | }
52 |
53 | static const struct wl_registry_listener registry_listener = {
54 | .global = registry_add,
55 | .global_remove = registry_remove,
56 | };
57 |
58 | static void receive_view_info(void *data,
59 | struct wf_info_base *wf_info_base,
60 | const uint32_t view_id,
61 | const int client_pid,
62 | const int ws_x,
63 | const int ws_y,
64 | const char *app_id,
65 | const char *title,
66 | const char *role,
67 | const int x,
68 | const int y,
69 | const int width,
70 | const int height,
71 | const int xwayland,
72 | const int focused,
73 | const char * output_name,
74 | const uint32_t output_id)
75 | {
76 | std::cout << "=========================" << std::endl;
77 | std::cout << "View ID: " << view_id << std::endl;
78 | std::cout << "Client PID: " << client_pid << std::endl;
79 | std::cout << "Output: " << output_name << "(ID: " << output_id << ")" << std::endl;
80 | std::cout << "Workspace: " << ws_x << "," << ws_y << std::endl;
81 | std::cout << "App ID: " << app_id << std::endl;
82 | std::cout << "Title: " << title << std::endl;
83 | std::cout << "Role: " << role << std::endl;
84 | std::cout << "Geometry: " << x << "," << y << " " << width << "x" << height << std::endl;
85 | std::cout << "Xwayland: " << (xwayland ? "true" : "false") << std::endl;
86 | std::cout << "Focused: " << (focused ? "true" : "false") << std::endl;
87 | std::cout << "=========================" << std::endl;
88 | }
89 |
90 | static void done(void *data,
91 | struct wf_info_base *wf_info_base)
92 | {
93 | exit(0);
94 | }
95 |
96 | static struct wf_info_base_listener information_base_listener {
97 | .view_info = receive_view_info,
98 | .done = done,
99 | };
100 |
101 | WfInfo::WfInfo(int argc, char *argv[])
102 | {
103 | display = wl_display_connect(NULL);
104 | if (!display)
105 | {
106 | return;
107 | }
108 |
109 | wl_registry *registry = wl_display_get_registry(display);
110 | if (!registry)
111 | {
112 | return;
113 | }
114 |
115 | wl_registry_add_listener(registry, ®istry_listener, this);
116 |
117 | wf_information_manager = NULL;
118 | wl_display_roundtrip(display);
119 | wl_registry_destroy(registry);
120 | if (!wf_information_manager)
121 | {
122 | std::cout << "Wayfire information protocol not advertised by compositor. Is wf-info plugin enabled?" << std::endl;
123 | return;
124 | }
125 |
126 | wf_info_base_add_listener(wf_information_manager,
127 | &information_base_listener, this);
128 |
129 | struct option opts[] = {
130 | { "view-id", required_argument, NULL, 'i' },
131 | { "all-views", no_argument, NULL, 'l' },
132 | { 0, 0, NULL, 0 }
133 | };
134 |
135 | std::vector view_ids;
136 | int c, i, list_all_views = 0;
137 | while((c = getopt_long(argc, argv, "i:l", opts, &i)) != -1)
138 | {
139 | switch(c)
140 | {
141 | case 'i':
142 | view_ids.push_back(atoi(optarg));
143 | break;
144 |
145 | case 'l':
146 | list_all_views = 1;
147 | break;
148 |
149 | default:
150 | printf("Unsupported command line argument %s\n", optarg);
151 | }
152 | }
153 |
154 | for (auto view_id : view_ids)
155 | {
156 | wf_info_base_view_info_id(wf_information_manager, view_id);
157 | }
158 |
159 | if (list_all_views)
160 | {
161 | wf_info_base_view_info_list(wf_information_manager);
162 | }
163 | else if (view_ids.empty())
164 | {
165 | wf_info_base_view_info(wf_information_manager);
166 | }
167 |
168 | while(1)
169 | wl_display_dispatch(display);
170 |
171 | wl_display_flush(display);
172 | wl_display_disconnect(display);
173 | }
174 |
175 | WfInfo::~WfInfo()
176 | {
177 | }
178 |
179 | int main(int argc, char *argv[])
180 | {
181 | WfInfo(argc, argv);
182 |
183 | return 0;
184 | }
185 |
--------------------------------------------------------------------------------
/src/client/wf-info.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2022 Scott Moreau
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 |
26 | #pragma once
27 |
28 | #include "wayfire-information-client-protocol.h"
29 |
30 | class WfInfo
31 | {
32 | public:
33 | WfInfo(int argc, char *argv[]);
34 | ~WfInfo();
35 |
36 | wl_display *display;
37 | wf_info_base *wf_information_manager;
38 | };
39 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2023 Scott Moreau
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 |
26 | #include
27 | #include
28 | #include
29 |
30 | #include "plugin/wayfire-information.hpp"
31 |
32 | class wf_info : public wf::plugin_interface_t, public wf::pointer_interaction_t
33 | {
34 | public:
35 | std::unique_ptr wayfire_information_ptr = nullptr;
36 |
37 | void init()
38 | {
39 | wayfire_information_ptr = std::make_unique();
40 | wayfire_information_ptr->set_base_ptr(this);
41 | }
42 |
43 | void handle_pointer_button(const wlr_pointer_button_event& event) override
44 | {
45 | if (event.state == WL_POINTER_BUTTON_STATE_PRESSED)
46 | {
47 | wayfire_information_ptr->end_grab();
48 | }
49 | }
50 |
51 | void fini()
52 | {
53 | wayfire_information_ptr.reset();
54 | }
55 | };
56 |
57 | DECLARE_WAYFIRE_PLUGIN(wf_info);
58 |
--------------------------------------------------------------------------------
/src/meson.build:
--------------------------------------------------------------------------------
1 | sources = ['main.cpp', 'plugin/wayfire-information.cpp']
2 |
3 | wf_info = shared_module('wf-info', sources,
4 | dependencies: [wayfire, wf_server_protos],
5 | install: true, install_dir: join_paths(get_option('libdir'), 'wayfire'))
6 |
7 | subdir('client')
8 |
--------------------------------------------------------------------------------
/src/plugin/ipc-rules-common.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | static inline wf::json_t output_to_json(wf::output_t *o)
13 | {
14 | if (!o)
15 | {
16 | return wf::json_t::null();
17 | }
18 |
19 | wf::json_t response;
20 | response["id"] = o->get_id();
21 | response["name"] = o->to_string();
22 | response["geometry"] = wf::ipc::geometry_to_json(o->get_layout_geometry());
23 | response["workarea"] = wf::ipc::geometry_to_json(o->workarea->get_workarea());
24 | response["wset-index"] = o->wset()->get_index();
25 | response["workspace"]["x"] = o->wset()->get_current_workspace().x;
26 | response["workspace"]["y"] = o->wset()->get_current_workspace().y;
27 | response["workspace"]["grid_width"] = o->wset()->get_workspace_grid_size().width;
28 | response["workspace"]["grid_height"] = o->wset()->get_workspace_grid_size().height;
29 | return response;
30 | }
31 |
32 | static inline pid_t get_view_pid(wayfire_view view)
33 | {
34 | pid_t pid = -1;
35 | if (!view)
36 | {
37 | return pid;
38 | }
39 |
40 | #if WF_HAS_XWAYLAND
41 | wlr_surface *wlr_surface = view->get_wlr_surface();
42 | if (wlr_surface && wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))
43 | {
44 | pid = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface)->pid;
45 | } else
46 | #endif
47 | if (view && view->get_client())
48 | {
49 | wl_client_get_credentials(view->get_client(), &pid, 0, 0);
50 | }
51 |
52 | return pid; // NOLINT
53 | }
54 |
55 | static inline wf::geometry_t get_view_base_geometry(wayfire_view view)
56 | {
57 | auto sroot = view->get_surface_root_node();
58 | for (auto& ch : sroot->get_children())
59 | {
60 | if (auto wlr_surf = dynamic_cast(ch.get()))
61 | {
62 | auto bbox = wlr_surf->get_bounding_box();
63 | wf::pointf_t origin = sroot->to_global({0, 0});
64 | bbox.x = origin.x;
65 | bbox.y = origin.y;
66 | return bbox;
67 | }
68 | }
69 |
70 | return sroot->get_bounding_box();
71 | }
72 |
73 | static inline std::string role_to_string(enum wf::view_role_t role)
74 | {
75 | switch (role)
76 | {
77 | case wf::VIEW_ROLE_TOPLEVEL:
78 | return "toplevel";
79 |
80 | case wf::VIEW_ROLE_UNMANAGED:
81 | return "unmanaged";
82 |
83 | case wf::VIEW_ROLE_DESKTOP_ENVIRONMENT:
84 | return "desktop-environment";
85 |
86 | default:
87 | return "unknown";
88 | }
89 | }
90 |
91 | static inline std::string layer_to_string(std::optional layer)
92 | {
93 | if (!layer.has_value())
94 | {
95 | return "none";
96 | }
97 |
98 | switch (layer.value())
99 | {
100 | case wf::scene::layer::BACKGROUND:
101 | return "background";
102 |
103 | case wf::scene::layer::BOTTOM:
104 | return "bottom";
105 |
106 | case wf::scene::layer::WORKSPACE:
107 | return "workspace";
108 |
109 | case wf::scene::layer::TOP:
110 | return "top";
111 |
112 | case wf::scene::layer::UNMANAGED:
113 | return "unmanaged";
114 |
115 | case wf::scene::layer::OVERLAY:
116 | return "overlay";
117 |
118 | case wf::scene::layer::LOCK:
119 | return "lock";
120 |
121 | case wf::scene::layer::DWIDGET:
122 | return "dew";
123 |
124 | default:
125 | break;
126 | }
127 |
128 | wf::dassert(false, "invalid layer!");
129 | assert(false); // prevent compiler warning
130 | }
131 |
132 | static inline std::string get_view_type(wayfire_view view)
133 | {
134 | if (view->role == wf::VIEW_ROLE_TOPLEVEL)
135 | {
136 | return "toplevel";
137 | }
138 |
139 | if (view->role == wf::VIEW_ROLE_UNMANAGED)
140 | {
141 | #if WF_HAS_XWAYLAND
142 | auto surf = view->get_wlr_surface();
143 | if (surf && wlr_xwayland_surface_try_from_wlr_surface(surf))
144 | {
145 | return "x-or";
146 | }
147 |
148 | #endif
149 |
150 | return "unmanaged";
151 | }
152 |
153 | auto layer = wf::get_view_layer(view);
154 | if ((layer == wf::scene::layer::BACKGROUND) || (layer == wf::scene::layer::BOTTOM))
155 | {
156 | return "background";
157 | } else if (layer == wf::scene::layer::TOP)
158 | {
159 | return "panel";
160 | } else if (layer == wf::scene::layer::OVERLAY)
161 | {
162 | return "overlay";
163 | }
164 |
165 | return "unknown";
166 | }
167 |
168 | static inline wf::json_t view_to_json(wayfire_view view)
169 | {
170 | if (!view)
171 | {
172 | return wf::json_t::null();
173 | }
174 |
175 | auto output = view->get_output();
176 | wf::json_t description;
177 | description["id"] = view->get_id();
178 | description["pid"] = get_view_pid(view);
179 | description["title"] = view->get_title();
180 | description["app-id"] = view->get_app_id();
181 | description["base-geometry"] = wf::ipc::geometry_to_json(get_view_base_geometry(view));
182 | auto toplevel = wf::toplevel_cast(view);
183 | description["parent"] = toplevel && toplevel->parent ? (int)toplevel->parent->get_id() : -1;
184 | description["geometry"] =
185 | wf::ipc::geometry_to_json(toplevel ? toplevel->get_pending_geometry() : view->get_bounding_box());
186 | description["bbox"] = wf::ipc::geometry_to_json(view->get_bounding_box());
187 | description["output-id"] = view->get_output() ? view->get_output()->get_id() : -1;
188 | description["output-name"] = output ? output->to_string() : "null";
189 | description["last-focus-timestamp"] = wf::get_focus_timestamp(view);
190 | description["role"] = role_to_string(view->role);
191 | description["mapped"] = view->is_mapped();
192 | description["layer"] = layer_to_string(get_view_layer(view));
193 | description["tiled-edges"] = toplevel ? toplevel->pending_tiled_edges() : 0;
194 | description["fullscreen"] = toplevel ? toplevel->pending_fullscreen() : false;
195 | description["minimized"] = toplevel ? toplevel->minimized : false;
196 | description["activated"] = toplevel ? toplevel->activated : false;
197 | description["sticky"] = toplevel ? toplevel->sticky : false;
198 | description["wset-index"] = toplevel && toplevel->get_wset() ?
199 | static_cast(toplevel->get_wset()->get_index()) :
200 | -1;
201 | description["min-size"] = wf::ipc::dimensions_to_json(
202 | toplevel ? toplevel->toplevel()->get_min_size() : wf::dimensions_t{0, 0});
203 | description["max-size"] = wf::ipc::dimensions_to_json(
204 | toplevel ? toplevel->toplevel()->get_max_size() : wf::dimensions_t{0, 0});
205 | description["focusable"] = view->is_focusable();
206 | description["type"] = get_view_type(view);
207 |
208 | return description;
209 | }
210 |
211 | static inline wf::json_t wset_to_json(wf::workspace_set_t *wset)
212 | {
213 | if (!wset)
214 | {
215 | return wf::json_t::null();
216 | }
217 |
218 | wf::json_t response;
219 | response["index"] = wset->get_index();
220 | response["name"] = wset->to_string();
221 |
222 | auto output = wset->get_attached_output();
223 | response["output-id"] = output ? (int)output->get_id() : -1;
224 | response["output-name"] = output ? output->to_string() : "";
225 | response["workspace"]["x"] = wset->get_current_workspace().x;
226 | response["workspace"]["y"] = wset->get_current_workspace().y;
227 | response["workspace"]["grid_width"] = wset->get_workspace_grid_size().width;
228 | response["workspace"]["grid_height"] = wset->get_workspace_grid_size().height;
229 | return response;
230 | }
231 |
--------------------------------------------------------------------------------
/src/plugin/wayfire-information.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2023 Scott Moreau
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 |
41 | #include "wayfire-information.hpp"
42 | #include "wayfire-information-server-protocol.h"
43 |
44 | extern "C"
45 | {
46 | #include
47 | }
48 |
49 | wf::plugin_activation_data_t grab_interface{
50 | .name = "wf-info",
51 | .capabilities = wf::CAPABILITY_GRAB_INPUT,
52 | };
53 |
54 | static void bind_manager(wl_client *client, void *data,
55 | uint32_t version, uint32_t id);
56 |
57 | void wayfire_information::send_view_info(wayfire_view view)
58 | {
59 | if (!view)
60 | {
61 | if (ipc_call)
62 | {
63 | ipc_response = wf::ipc::json_error("No view found");
64 | ipc_call = false;
65 | }
66 | return;
67 | }
68 | if (ipc_call)
69 | {
70 | ipc_response = wf::ipc::json_ok();
71 | ipc_response["info"] = view_to_json(view);
72 | ipc_call = false;
73 | return;
74 | }
75 | auto output = view->get_output();
76 | if (!output)
77 | {
78 | return;
79 | }
80 |
81 | std::string role;
82 | switch (view->role)
83 | {
84 | case wf::VIEW_ROLE_TOPLEVEL:
85 | role = "TOPLEVEL";
86 | break;
87 | case wf::VIEW_ROLE_UNMANAGED:
88 | role = "UNMANAGED";
89 | break;
90 | case wf::VIEW_ROLE_DESKTOP_ENVIRONMENT:
91 | role = "DESKTOP_ENVIRONMENT";
92 | break;
93 | default:
94 | role = "UNKNOWN";
95 | break;
96 | }
97 |
98 | auto og = output->get_screen_size();
99 | auto ws = output->wset()->get_current_workspace();
100 | auto wm = wf::view_bounding_box_up_to(view);
101 | wf::point_t workspace = {
102 | ws.x + (int)std::floor((wm.x + wm.width / 2.0) / og.width),
103 | ws.y + (int)std::floor((wm.y + wm.height / 2.0) / og.height)
104 | };
105 |
106 | auto toplevel = toplevel_cast(view);
107 | wf::geometry_t vg{0, 0, 0, 0};
108 | if (toplevel)
109 | {
110 | vg = toplevel->get_geometry();
111 | }
112 |
113 | pid_t pid = -1;
114 | wlr_surface *wlr_surface = view->get_wlr_surface();
115 | int is_xwayland_surface = 0;
116 | #if WF_HAS_XWAYLAND
117 | is_xwayland_surface = wlr_surface && wlr_xwayland_surface_try_from_wlr_surface(wlr_surface);
118 | if (is_xwayland_surface)
119 | {
120 | pid = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface)->pid;
121 | } else
122 | #endif
123 | {
124 | if (view->get_client())
125 | {
126 | wl_client_get_credentials(view->get_client(), &pid, 0, 0);
127 | }
128 | }
129 |
130 | int focused = wf::get_active_view_for_output(output) == view;
131 |
132 | for (auto r : client_resources)
133 | {
134 | wf_info_base_send_view_info(r, view->get_id(),
135 | pid,
136 | workspace.x,
137 | workspace.y,
138 | view->get_app_id().c_str(),
139 | view->get_title().c_str(),
140 | role.c_str(),
141 | vg.x,
142 | vg.y,
143 | vg.width,
144 | vg.height,
145 | is_xwayland_surface,
146 | focused,
147 | output->to_string().c_str(),
148 | output->get_id());
149 | }
150 | }
151 |
152 | void wayfire_information::deactivate()
153 | {
154 | for (auto& o : wf::get_core().output_layout->get_outputs())
155 | {
156 | o->deactivate_plugin(&grab_interface);
157 | input_grabs[o]->ungrab_input();
158 | input_grabs[o].reset();
159 | }
160 |
161 | idle_set_cursor.run_once([this] ()
162 | {
163 | wf::get_core().set_cursor("default");
164 | send_view_info(wf::get_core().get_cursor_focus_view());
165 | for (auto r : client_resources)
166 | {
167 | wf_info_base_send_done(r);
168 | }
169 | });
170 | wl_call = false;
171 | }
172 |
173 | void wayfire_information::end_grab()
174 | {
175 | deactivate();
176 | }
177 |
178 | void wayfire_information::set_base_ptr(wf::pointer_interaction_t *base)
179 | {
180 | this->base = base;
181 | }
182 |
183 | wayfire_information::wayfire_information()
184 | {
185 | manager = wl_global_create(wf::get_core().display,
186 | &wf_info_base_interface, 1, this, bind_manager);
187 |
188 | if (!manager)
189 | {
190 | LOGE("Failed to create wayfire_information interface");
191 | return;
192 | }
193 |
194 | get_view_info_ipc = [=] (wf::json_t data)
195 | {
196 | if (ipc_call)
197 | {
198 | return wf::ipc::json_error("Another ipc grab is already active.");
199 | }
200 | for (auto& o : wf::get_core().output_layout->get_outputs())
201 | {
202 | input_grabs[o] = std::make_unique (grab_interface.name, o, nullptr, base, nullptr);
203 |
204 | if (!o->activate_plugin(&grab_interface))
205 | {
206 | continue;
207 | }
208 |
209 | input_grabs[o]->grab_input(wf::scene::layer::OVERLAY);
210 | }
211 |
212 | idle_set_cursor.run_once([=] ()
213 | {
214 | wf::get_core().set_cursor("crosshair");
215 | });
216 |
217 | ipc_call = true;
218 | while (ipc_call)
219 | {
220 | wl_event_loop_dispatch(wf::get_core().ev_loop, 0);
221 | }
222 |
223 | return ipc_response;
224 | };
225 |
226 | ipc_repo->register_method("wf-info/get_view_info", get_view_info_ipc);
227 | }
228 |
229 | wayfire_information::~wayfire_information()
230 | {
231 | wl_global_destroy(manager);
232 |
233 | for (auto& o : wf::get_core().output_layout->get_outputs())
234 | {
235 | input_grabs[o].reset();
236 | }
237 | }
238 |
239 | wayfire_view view_from_id(int32_t id)
240 | {
241 | if (id == -1)
242 | {
243 | return wf::get_active_view_for_output(wf::get_core().seat->get_active_output());
244 | }
245 |
246 | for (auto& view : wf::get_core().get_all_views())
247 | {
248 | if (int32_t(view->get_id()) == id)
249 | {
250 | return view;
251 | }
252 | }
253 |
254 | return nullptr;
255 | }
256 |
257 | static void get_view_info(struct wl_client *client, struct wl_resource *resource)
258 | {
259 | wayfire_information *wd = (wayfire_information*)wl_resource_get_user_data(resource);
260 |
261 | if (wd->wl_call)
262 | {
263 | return;
264 | }
265 | wd->wl_call = true;
266 |
267 | for (auto& o : wf::get_core().output_layout->get_outputs())
268 | {
269 | wd->input_grabs[o] = std::make_unique (grab_interface.name, o, nullptr, wd->base, nullptr);
270 |
271 | if (!o->activate_plugin(&grab_interface))
272 | {
273 | continue;
274 | }
275 |
276 | wd->input_grabs[o]->grab_input(wf::scene::layer::OVERLAY);
277 | }
278 |
279 | wd->idle_set_cursor.run_once([wd] ()
280 | {
281 | wf::get_core().set_cursor("crosshair");
282 | });
283 | }
284 |
285 | static void send_view_info_from_id(struct wl_client *client, struct wl_resource *resource, int id)
286 | {
287 | wayfire_information *wd = (wayfire_information*)wl_resource_get_user_data(resource);
288 |
289 | auto view = view_from_id(id);
290 |
291 | if (!view)
292 | {
293 | return;
294 | }
295 |
296 | wd->send_view_info(view);
297 |
298 | for (auto r : wd->client_resources)
299 | {
300 | wf_info_base_send_done(r);
301 | }
302 | }
303 |
304 | static void send_all_views(struct wl_client *client, struct wl_resource *resource)
305 | {
306 | wayfire_information *wd = (wayfire_information*)wl_resource_get_user_data(resource);
307 |
308 | for (auto& view : wf::get_core().get_all_views())
309 | {
310 | if (view->role != wf::VIEW_ROLE_TOPLEVEL &&
311 | view->role != wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)
312 | {
313 | continue;
314 | }
315 | wd->send_view_info(view);
316 | }
317 |
318 | for (auto r : wd->client_resources)
319 | {
320 | wf_info_base_send_done(r);
321 | }
322 | }
323 |
324 | static const struct wf_info_base_interface wayfire_information_impl =
325 | {
326 | .view_info = get_view_info,
327 | .view_info_id = send_view_info_from_id,
328 | .view_info_list = send_all_views,
329 | };
330 |
331 | static void destroy_client(wl_resource *resource)
332 | {
333 | wayfire_information *wd = (wayfire_information*)wl_resource_get_user_data(resource);
334 |
335 | for (auto& r : wd->client_resources)
336 | {
337 | if (r == resource)
338 | {
339 | r = nullptr;
340 | }
341 | }
342 | wd->client_resources.erase(std::remove(wd->client_resources.begin(),
343 | wd->client_resources.end(), nullptr), wd->client_resources.end());
344 | }
345 |
346 | static void bind_manager(wl_client *client, void *data,
347 | uint32_t version, uint32_t id)
348 | {
349 | wayfire_information *wd = (wayfire_information*)data;
350 |
351 | auto resource =
352 | wl_resource_create(client, &wf_info_base_interface, 1, id);
353 | wl_resource_set_implementation(resource,
354 | &wayfire_information_impl, data, destroy_client);
355 | wd->client_resources.push_back(resource);
356 |
357 | }
358 |
--------------------------------------------------------------------------------
/src/plugin/wayfire-information.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2023 Scott Moreau
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 |
26 | #pragma once
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include "ipc-rules-common.hpp"
33 |
34 | class wayfire_information
35 | {
36 | wl_global *manager;
37 |
38 | public:
39 | wf::pointer_interaction_t *base;
40 | std::vector client_resources;
41 | void send_view_info(wayfire_view view);
42 | void deactivate();
43 | void set_base_ptr(wf::pointer_interaction_t *base);
44 | wf::wl_idle_call idle_set_cursor;
45 | std::map> input_grabs;
46 | bool ipc_call = false;
47 | bool wl_call = false;
48 | wf::json_t ipc_response;
49 | wf::ipc::method_callback get_view_info_ipc;
50 | wf::shared_data::ref_ptr_t ipc_repo;
51 | void end_grab();
52 | wayfire_information();
53 | ~wayfire_information();
54 | };
55 |
--------------------------------------------------------------------------------