1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
extern crate byteorder;
extern crate tempfile;
#[macro_use(event_enum)]
extern crate wayland_client;
use std::cmp::min;
use std::io::Write;
use std::os::unix::io::AsRawFd;
use byteorder::{NativeEndian, WriteBytesExt};
use wayland_client::protocol::{wl_compositor, wl_keyboard, wl_pointer, wl_seat, wl_shell, wl_shm};
use wayland_client::{Display, Filter, GlobalManager};
// declare an event enum containing the events we want to receive in the iterator
event_enum!(
Events |
Pointer => wl_pointer::WlPointer,
Keyboard => wl_keyboard::WlKeyboard
);
fn main() {
let display = Display::connect_to_env().unwrap();
let mut event_queue = display.create_event_queue();
let attached_display = (*display).clone().attach(event_queue.get_token());
let globals = GlobalManager::new(&attached_display);
// roundtrip to retrieve the globals list
event_queue.sync_roundtrip(|_, _| unreachable!()).unwrap();
/*
* Create a buffer with window contents
*/
// buffer (and window) width and height
let buf_x: u32 = 320;
let buf_y: u32 = 240;
// create a tempfile to write the contents of the window on
let mut tmp = tempfile::tempfile().ok().expect("Unable to create a tempfile.");
// write the contents to it, lets put a nice color gradient
for i in 0..(buf_x * buf_y) {
let x = (i % buf_x) as u32;
let y = (i / buf_x) as u32;
let r: u32 = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
let g: u32 = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
let b: u32 = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
let _ = tmp.write_u32::<NativeEndian>((0xFF << 24) + (r << 16) + (g << 8) + b);
}
let _ = tmp.flush();
/*
* Init wayland objects
*/
// The compositor allows us to creates surfaces
let compositor = globals
.instantiate_exact::<wl_compositor::WlCompositor>(1)
.unwrap();
let surface = compositor.create_surface();
// The SHM allows us to share memory with the server, and create buffers
// on this shared memory to paint our surfaces
let shm = globals.instantiate_exact::<wl_shm::WlShm>(1).unwrap();
let pool = shm.create_pool(
tmp.as_raw_fd(), // RawFd to the tempfile serving as shared memory
(buf_x * buf_y * 4) as i32, // size in bytes of the shared memory (4 bytes per pixel)
);
let buffer = pool.create_buffer(
0, // Start of the buffer in the pool
buf_x as i32, // width of the buffer in pixels
buf_y as i32, // height of the buffer in pixels
(buf_x * 4) as i32, // number of bytes between the beginning of two consecutive lines
wl_shm::Format::Argb8888, // chosen encoding for the data
);
// The shell allows us to define our surface as a "toplevel", meaning the
// server will treat it as a window
//
// NOTE: the wl_shell interface is actually deprecated in favour of the xdg_shell
// protocol, available in wayland-protocols. But this will do for this example.
let shell = globals
.instantiate_exact::<wl_shell::WlShell>(1)
.expect("Compositor does not support wl_shell");
let shell_surface = shell.get_shell_surface(&surface);
shell_surface.assign_mono(|shell_surface, event| {
use wayland_client::protocol::wl_shell_surface::Event;
// This ping/pong mechanism is used by the wayland server to detect
// unresponsive applications
if let Event::Ping { serial } = event {
shell_surface.pong(serial);
}
});
// Set our surface as toplevel and define its contents
shell_surface.set_toplevel();
surface.attach(Some(&buffer), 0, 0);
surface.commit();
// initialize a seat to retrieve pointer & keyboard events
//
// example of using a common filter to handle both pointer & keyboard events
let common_filter = Filter::new(move |event, _| match event {
Events::Pointer { event, .. } => match event {
wl_pointer::Event::Enter {
surface_x, surface_y, ..
} => {
println!("Pointer entered at ({}, {}).", surface_x, surface_y);
}
wl_pointer::Event::Leave { .. } => {
println!("Pointer left.");
}
wl_pointer::Event::Motion {
surface_x, surface_y, ..
} => {
println!("Pointer moved to ({}, {}).", surface_x, surface_y);
}
wl_pointer::Event::Button { button, state, .. } => {
println!("Button {} was {:?}.", button, state);
}
_ => {}
},
Events::Keyboard { event, .. } => match event {
wl_keyboard::Event::Enter { .. } => {
println!("Gained keyboard focus.");
}
wl_keyboard::Event::Leave { .. } => {
println!("Lost keyboard focus.");
}
wl_keyboard::Event::Key { key, state, .. } => {
println!("Key with id {} was {:?}.", key, state);
}
_ => (),
},
});
// to be handled properly this should be more dynamic, as more
// than one seat can exist (and they can be created and destroyed
// dynamically), however most "traditional" setups have a single
// seat, so we'll keep it simple here
let mut pointer_created = false;
let mut keyboard_created = false;
globals
.instantiate_exact::<wl_seat::WlSeat>(1)
.unwrap()
.assign_mono(move |seat, event| {
// The capabilities of a seat are known at runtime and we retrieve
// them via an events. 3 capabilities exists: pointer, keyboard, and touch
// we are only interested in pointer & keyboard here
use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent};
if let SeatEvent::Capabilities { capabilities } = event {
if !pointer_created && capabilities.contains(Capability::Pointer) {
// create the pointer only once
pointer_created = true;
seat.get_pointer().assign(common_filter.clone());
}
if !keyboard_created && capabilities.contains(Capability::Keyboard) {
// create the keyboard only once
keyboard_created = false;
seat.get_keyboard().assign(common_filter.clone());
}
}
});
event_queue
.sync_roundtrip(|_, _| { /* we ignore unfiltered messages */ })
.unwrap();
loop {
event_queue
.dispatch(|_, _| { /* we ignore unfiltered messages */ })
.unwrap();
}
}