[−][src]Module breadx::tutorials::event
Last time, we implemented a basic window. Today, we'll add event handling to that window, and use that to add some functionality.
If requests and replies are the blood of X, events are the nerves. It's how you react to the outside world. So far, our window doesn't really do much outside of exist. Let's change that.
Note that we already handle an event: the ClientMessageEvent
required to close the window.
match ev { Event::ClientMessage(cme) => { if cme.data.longs()[0] == wm_delete_window.xid { break; } } _ => (), }
It's slightly more complicated to handle other events.
Event Masks
Before we dive into events, we need to talk about event masks. In order to avoid sending events that the
client is not interesting in, X uses a "mask" value to determine which events to send to the client. This
type is represented by the EventMask
structure.
TODO: rework this section once I finalize how bitflags are going to be represented
After constructing the event mask, the Window::set_event_mask
function is used to set the event mask for a particular window. If you have more than one
window, each needs its own event mask.
use breadx::{BreadError, Event, EventMask, DisplayConnection}; fn main() -> Result<(), BreadError> { // simplified form of the code from last tutorial let mut conn = DisplayConnection::create(None, None)?; let window = conn.create_simple_window( conn.default_root(), 0, 0, 640, 400, 0, conn.default_black_pixel(), conn.default_white_pixel(), )?; window.set_title(&mut conn, "Event Mask")?; window.map(&mut conn)?; let wm_delete_window = conn .intern_atom_immediate("WM_DELETE_WINDOW".to_owned(), false)?; window.set_wm_protocols(&mut conn, &[wm_delete_window])?; // NEW: create an event mask that listens for ButtonPress events // the default event mask is empty let mut event_mask: EventMask = Default::default(); event_mask.set_button_press(true); // NEW: call set_event_mask on the window window.set_event_mask(&mut conn, event_mask)?; loop { let ev = conn.wait_for_event()?; match ev { Event::ClientMessage(cme) => { if cme.data.longs()[0] == wm_delete_window.xid { break; } } // NEW: listen for Event::ButtonPress, then print the click location to the console Event::ButtonPress(bpe) => { println!("Clicked at {}, {}", bpe.event_x, bpe.event_y); } _ => (), } } Ok(()) }
If you run this program and click on the resulting window, it will display the following output, depending on where you clicked:
Clicked at 381, 232
Clicked at 474, 132
Clicked at 281, 121
Clicked at 224, 285
Clicked at 366, 324
Clicked at 7, 7
Now, try removing the set_button_press
call, and note that you no longer receive button press events.
Another Perspective on `EventMask`
TODO: implementation of bitflags explanation
Events
The Event
enum can contain every event defined by the X Core Protocol, as
well as a NoneOfTheAbove
bit collection that is reserved to events either sent by the user or from
unrecognized extensions.
This section will break down the core events used by breadx
, and how they ought to be handled.
ClientMessageEvent
ClientMessageEvent
, at first, meant "reserved for the
client to use as they see fit". It can also be used to convey window manager messages. We've already used
this one as a way of responding to window close events.
The key point is the data
field, which is of type ClientMessageData
. It can represent 5 32-bit numbers, 10 16-bit numbers, or 20 8-bit numbers.
Usually, you shouldn't listen for this one unless you explicitly know you need to listen for it.
ButtonPressEvent
and ButtonReleaseEvent
As one would expect, ButtonPressEvent
represents one of
the mouse buttons being pressed. The detail
field tells you which button is being pressed, from 1 to 5.
event_x
and event_y
tell you the click's location relative to the origin of the window (top-left
corner), while root_x
and root_y
tell you the click's location relative to the origin of the screen.
The inverse of this event, ButtonReleaseEvent
, gives
you these details when the mouse button is released.
ExposeEvent
ExposeEvent
occurs when the window is being repainted. We'll
discuss this in more detail when we discuss drawing, but for now keep in mind that you should draw on the
window whenever you see this event.
TODO: populate with other events
Next time, we'll talk about color management and the two primary approaches to creating color.