drm/event_handler.rs
1// Copyright 2016 The libdrm-rs project developers
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
4// and associated documentation files (the "Software"), to deal in the Software without
5// restriction, including without limitation the rights to use, copy, modify, merge, publish,
6// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
7// Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
13// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
15// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
18//! In C `drmHandleEvent` takes as an argument pointer to context containing two optional pointers
19//! to callback functions handling v-blanks or page flips. It reads events from DRM device and call
20//! appropriate callback with no other state than passed to `drmModePageFlip`. Any other state must
21//! be global.
22//!
23//! In Rust we try avoid forcing users to implement global states or singletons, however passing
24//! state by `drmModePageFlip` would make interface unsafe. We want to have safe interface but also
25//! provide users context for their callbacks without forcing them to implement global state.
26//!
27//! This problem is solved by taking context in `handle_event` function wrapping `drmHandleEvent`
28//! and storing it in thread local storage for time of processing events. User callbacks are then
29//! called within this context.
30
31use libc;
32use std::cell::RefCell;
33use std::os::unix::io;
34
35use ffi;
36
37pub const DRM_CONTEXT_VERSION: libc::c_int = 2; /**< Desired DRM event context version */
38
39/// Trait for contexts passed to `handle_event`.
40pub trait EventContext {
41 fn vblank_handler(&mut self, fd: io::RawFd, sequence: u32, sec: u32, usec: u32, data: i32);
42 fn page_flip_handler(&mut self, fd: io::RawFd, sequence: u32, sec: u32, usec: u32, data: i32);
43}
44
45/// Thread local storage for event contexts.
46thread_local!(static CONTEXT: RefCell<Option<Box<EventContext>>> = RefCell::new(None));
47
48/// Helper function for handling v-blanks.
49extern "C" fn vblank_handler(fd: libc::c_int,
50 sequence: libc::c_uint,
51 tv_sec: libc::c_uint,
52 tv_usec: libc::c_uint,
53 user_data: *mut libc::c_void) {
54 CONTEXT.with(|s| if let Some(ref mut context) = *s.borrow_mut() {
55 context.vblank_handler(fd, sequence, tv_sec, tv_usec, user_data as i32);
56 });
57}
58
59/// Helper function for handling page flips.
60extern "C" fn page_flip_handler(fd: libc::c_int,
61 sequence: libc::c_uint,
62 tv_sec: libc::c_uint,
63 tv_usec: libc::c_uint,
64 user_data: *mut libc::c_void) {
65 CONTEXT.with(|s| if let Some(ref mut context) = *s.borrow_mut() {
66 context.page_flip_handler(fd, sequence, tv_sec, tv_usec, user_data as i32);
67 });
68}
69
70/// Handle event from DRM device.
71///
72/// Counterpart for `drmHandleEvent`.
73pub fn handle_event(fd: io::RawFd, context: Box<EventContext>) {
74 CONTEXT.with(|s| *s.borrow_mut() = Some(context));
75
76 let mut drm_context = ffi::xf86drm::drmEventContext::default();
77 drm_context.version = DRM_CONTEXT_VERSION;
78 drm_context.vblank_handler = vblank_handler;
79 drm_context.page_flip_handler = page_flip_handler;
80 unsafe {
81 ffi::xf86drm::drmHandleEvent(fd, &drm_context);
82 }
83
84 CONTEXT.with(|s| *s.borrow_mut() = None);
85}