druid_win_shell/
win_main.rs

1// Copyright 2018 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Windows main loop.
16
17use std::mem;
18use std::ptr::null_mut;
19use std::sync::{Arc, Mutex};
20use winapi::shared::windef::*;
21use winapi::ctypes::c_int;
22use winapi::um::winbase::*;
23use winapi::um::winnt::*;
24use winapi::um::winuser::*;
25
26#[derive(Clone, Default)]
27pub struct RunLoopHandle(Arc<Mutex<RunLoopState>>);
28
29#[derive(Default)]
30struct RunLoopState {
31    listeners: Vec<Listener>,
32}
33
34// It's only safe to add listeners from the same thread as the runloop.
35unsafe impl Send for Listener {}
36struct Listener {
37    h: HANDLE,
38    callback: Box<FnMut()>,
39}
40
41pub struct RunLoop {
42    handle: RunLoopHandle,
43    accel: HACCEL,
44}
45
46impl RunLoop {
47    pub fn new() -> RunLoop {
48        RunLoop {
49            handle: Default::default(),
50            accel: null_mut(),
51        }
52    }
53
54    /// Get a handle to the run loop state so a client can add listeners,
55    /// etc.
56    pub fn get_handle(&self) -> RunLoopHandle {
57        self.handle.clone()
58    }
59
60    /// Set an accelerator table
61    pub fn set_accel(&mut self, accel: &[ACCEL]) {
62        unsafe {
63            self.accel = CreateAcceleratorTableW(accel as *const _ as *mut _,
64                accel.len() as c_int);
65        }
66    }
67
68    pub fn run(&mut self) {
69
70        unsafe {
71            loop {
72                let mut handles = Vec::new();
73                for listener in &self.handle.0.lock().unwrap().listeners {
74                    handles.push(listener.h);
75                }
76                let len = handles.len() as u32;
77                let res = MsgWaitForMultipleObjectsEx(
78                    len,
79                    handles.as_ptr(),
80                    INFINITE,
81                    QS_ALLEVENTS,
82                    0
83                );
84
85                // Prioritize rpc results above windows messages
86                if res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + len {
87                    let ix = (res - WAIT_OBJECT_0) as usize;
88                    (&mut self.handle.0.lock().unwrap().listeners[ix].callback)();
89                }
90
91                // Handle windows messages
92                loop {
93                    let mut msg = mem::uninitialized();
94                    // Note: we could use PM_REMOVE here and avoid the GetMessage below
95                    let res = PeekMessageW(&mut msg, null_mut(), 0, 0, PM_NOREMOVE);
96                    if res == 0 {
97                        break;
98                    }
99                    let res = GetMessageW(&mut msg, null_mut(), 0, 0);
100                    if res <= 0 {
101                        return;
102                    }
103                    if self.accel.is_null() ||
104                        TranslateAcceleratorW(msg.hwnd, self.accel, &mut msg) == 0
105                    {
106                        TranslateMessage(&mut msg);
107                        DispatchMessageW(&mut msg);
108                    }
109                }
110            }
111        }
112    }
113}
114
115/// Request to quit the application, exiting the runloop.
116pub fn request_quit() {
117    unsafe {
118        PostQuitMessage(0);
119    }
120}
121
122impl RunLoopHandle {
123    /// Add a listener for a Windows handle. Considered unsafe because the
124    /// handle must be valid. Also unsafe because it is not thread safe.
125    pub unsafe fn add_handler<F>(&self, h: HANDLE, callback: F)
126        where F: FnMut() + 'static
127    {
128        let listener = Listener {
129            h,
130            callback: Box::new(callback),
131        };
132        self.0.lock().unwrap().listeners.push(listener);
133    }
134}