Commit ade2a66b37f5c67523e2766a5facabf0bc632765
1 parent
e433cb13
Add some xcb examples.
Include a shm example which I created by analyse the C exmaples and try to reproduce with rust libc and rust-xcb.
Showing
4 changed files
with
647 additions
and
0 deletions
xcb-test/Cargo.toml
0 → 100644
| 1 | +[package] | |
| 2 | +name = "xcb-test" | |
| 3 | +version = "0.1.0" | |
| 4 | +authors = ["Georg Hopp <georg@steffers.org>"] | |
| 5 | +edition = "2018" | |
| 6 | + | |
| 7 | +[dependencies] | |
| 8 | +libc = "0.2" | |
| 9 | +gl = "0.5.2" | |
| 10 | +x11 = { version = "2.3", features = ["glx"] } | |
| 11 | +xcb = { version = "0.8", features = ["dri2", "randr", "thread", "xlib_xcb", "shm"] } | ... | ... |
xcb-test/alternatives/info.rs
0 → 100644
| 1 | +extern crate xcb; | |
| 2 | + | |
| 3 | +use std::iter::{Iterator}; | |
| 4 | +use xcb::randr; | |
| 5 | + | |
| 6 | +fn main() { | |
| 7 | + | |
| 8 | + let dpy = ":0"; | |
| 9 | + let (conn, screen_num) = xcb::Connection::connect(Some(&dpy)).unwrap(); | |
| 10 | + | |
| 11 | + let setup = conn.get_setup(); | |
| 12 | + let screen = setup.roots().nth(screen_num as usize).unwrap(); | |
| 13 | + | |
| 14 | + println!(""); | |
| 15 | + println!("Informations of screen {}:", screen.root()); | |
| 16 | + println!(" width..........: {}", screen.width_in_pixels()); | |
| 17 | + println!(" height.........: {}", screen.height_in_pixels()); | |
| 18 | + println!(" white pixel....: {:x}", screen.white_pixel()); | |
| 19 | + println!(" black pixel....: {:x}", screen.black_pixel()); | |
| 20 | + | |
| 21 | + let window_dummy = conn.generate_id(); | |
| 22 | + xcb::create_window( | |
| 23 | + &conn, 0, window_dummy, screen.root() | |
| 24 | + , 0, 0, 1, 1, 0, 0, 0, &[]); | |
| 25 | + | |
| 26 | + conn.flush(); | |
| 27 | + | |
| 28 | + let cookie = randr::get_screen_info(&conn, window_dummy); | |
| 29 | + let reply = cookie.get_reply().unwrap(); | |
| 30 | + let sizes = reply.sizes(); | |
| 31 | + | |
| 32 | + for (i, size) in sizes.enumerate() { | |
| 33 | + if i != 0 { println!(""); } | |
| 34 | + println!("size of screen {}:", i+1); | |
| 35 | + println!(" {} x {} ({}mm x {}mm)", size.width(), size.height(), | |
| 36 | + size.mwidth(), size.mheight()); | |
| 37 | + } | |
| 38 | + | |
| 39 | + // ==== | |
| 40 | + | |
| 41 | + let window = conn.generate_id(); | |
| 42 | + | |
| 43 | + let values = [ | |
| 44 | + (xcb::CW_BACK_PIXEL, screen.white_pixel()), | |
| 45 | + (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS), | |
| 46 | + ]; | |
| 47 | + | |
| 48 | + xcb::create_window(&conn, | |
| 49 | + xcb::COPY_FROM_PARENT as u8, | |
| 50 | + window, | |
| 51 | + screen.root(), | |
| 52 | + 0, 0, | |
| 53 | + 150, 150, | |
| 54 | + 10, | |
| 55 | + xcb::WINDOW_CLASS_INPUT_OUTPUT as u16, | |
| 56 | + screen.root_visual(), | |
| 57 | + &values); | |
| 58 | + | |
| 59 | + xcb::map_window(&conn, window); | |
| 60 | + | |
| 61 | + let title = "Basic Window"; | |
| 62 | + // setting title | |
| 63 | + xcb::change_property(&conn, xcb::PROP_MODE_REPLACE as u8, window, | |
| 64 | + xcb::ATOM_WM_NAME, xcb::ATOM_STRING, 8, title.as_bytes()); | |
| 65 | + | |
| 66 | + conn.flush(); | |
| 67 | + | |
| 68 | + // retrieving title | |
| 69 | + let cookie = xcb::get_property(&conn, false, window, xcb::ATOM_WM_NAME, | |
| 70 | + xcb::ATOM_STRING, 0, 1024); | |
| 71 | + if let Ok(reply) = cookie.get_reply() { | |
| 72 | + assert_eq!(std::str::from_utf8(reply.value()).unwrap(), title); | |
| 73 | + } else { | |
| 74 | + panic!("could not retrieve window title!"); | |
| 75 | + } | |
| 76 | + | |
| 77 | + // retrieving a few atoms | |
| 78 | + let (wm_state, wm_state_maxv, wm_state_maxh) = { | |
| 79 | + let cook = xcb::intern_atom(&conn, true, "_NET_WM_STATE"); | |
| 80 | + let cook_maxv = xcb::intern_atom(&conn, true, "_NET_WM_STATE_MAXIMIZED_VERT"); | |
| 81 | + let cook_maxh = xcb::intern_atom(&conn, true, "_NET_WM_STATE_MAXIMIZED_HORZ"); | |
| 82 | + | |
| 83 | + (cook.get_reply().unwrap().atom(), | |
| 84 | + cook_maxv.get_reply().unwrap().atom(), | |
| 85 | + cook_maxh.get_reply().unwrap().atom()) | |
| 86 | + }; | |
| 87 | + | |
| 88 | + let mut maximized = false; | |
| 89 | + | |
| 90 | + loop { | |
| 91 | + let event = conn.wait_for_event(); | |
| 92 | + match event { | |
| 93 | + None => { break; } | |
| 94 | + Some(event) => { | |
| 95 | + let r = event.response_type(); | |
| 96 | + if r == xcb::KEY_PRESS as u8 { | |
| 97 | + let key_press : &xcb::KeyPressEvent = unsafe { | |
| 98 | + xcb::cast_event(&event) | |
| 99 | + }; | |
| 100 | + | |
| 101 | + println!("Key '{}' pressed", key_press.detail()); | |
| 102 | + | |
| 103 | + if key_press.detail() == 0x3a { // M (on qwerty) | |
| 104 | + | |
| 105 | + // toggle maximized | |
| 106 | + println!("toggle maximized: {} {}", wm_state_maxv, wm_state_maxh); | |
| 107 | + | |
| 108 | + // ClientMessageData is a memory safe untagged union | |
| 109 | + let data = xcb::ClientMessageData::from_data32([ | |
| 110 | + if maximized { 0 } else { 1 }, | |
| 111 | + wm_state_maxv, wm_state_maxh, | |
| 112 | + 0, 0 | |
| 113 | + ]); | |
| 114 | + | |
| 115 | + let ev = xcb::ClientMessageEvent::new(32, window, | |
| 116 | + wm_state, data); | |
| 117 | + | |
| 118 | + xcb::send_event(&conn, false, screen.root(), | |
| 119 | + xcb::EVENT_MASK_STRUCTURE_NOTIFY, &ev); | |
| 120 | + | |
| 121 | + conn.flush(); | |
| 122 | + | |
| 123 | + maximized = !maximized; | |
| 124 | + } | |
| 125 | + else if key_press.detail() == 0x18 { // Q (on qwerty) | |
| 126 | + break; | |
| 127 | + } | |
| 128 | + } | |
| 129 | + } | |
| 130 | + } | |
| 131 | + } | |
| 132 | +} | ... | ... |
xcb-test/alternatives/opengl.rs
0 → 100644
| 1 | + | |
| 2 | +extern crate x11; | |
| 3 | +extern crate xcb; | |
| 4 | +extern crate gl; | |
| 5 | +extern crate libc; | |
| 6 | + | |
| 7 | +use xcb::dri2; | |
| 8 | + | |
| 9 | +use x11::xlib; | |
| 10 | +use x11::glx::*; | |
| 11 | + | |
| 12 | +use std::ptr::null_mut; | |
| 13 | +use std::ffi::{CStr, CString}; | |
| 14 | +use std::os::raw::{c_int, c_void}; | |
| 15 | + | |
| 16 | + | |
| 17 | +const GLX_CONTEXT_MAJOR_VERSION_ARB: u32 = 0x2091; | |
| 18 | +const GLX_CONTEXT_MINOR_VERSION_ARB: u32 = 0x2092; | |
| 19 | + | |
| 20 | +type GlXCreateContextAttribsARBProc = | |
| 21 | + unsafe extern "C" fn (dpy: *mut xlib::Display, fbc: GLXFBConfig, | |
| 22 | + share_context: GLXContext, direct: xlib::Bool, | |
| 23 | + attribs: *const c_int) -> GLXContext; | |
| 24 | + | |
| 25 | + | |
| 26 | +unsafe fn load_gl_func (name: &str) -> *mut c_void { | |
| 27 | + let cname = CString::new(name).unwrap(); | |
| 28 | + let ptr: *mut c_void = std::mem::transmute(glXGetProcAddress( | |
| 29 | + cname.as_ptr() as *const u8 | |
| 30 | + )); | |
| 31 | + if ptr.is_null() { | |
| 32 | + panic!("could not load {}", name); | |
| 33 | + } | |
| 34 | + ptr | |
| 35 | +} | |
| 36 | + | |
| 37 | +fn check_glx_extension(glx_exts: &str, ext_name: &str) -> bool { | |
| 38 | + for glx_ext in glx_exts.split(" ") { | |
| 39 | + if glx_ext == ext_name { | |
| 40 | + return true; | |
| 41 | + } | |
| 42 | + } | |
| 43 | + false | |
| 44 | +} | |
| 45 | + | |
| 46 | +static mut ctx_error_occurred: bool = false; | |
| 47 | +unsafe extern "C" fn ctx_error_handler( | |
| 48 | + _dpy: *mut xlib::Display, | |
| 49 | + _ev: *mut xlib::XErrorEvent) -> i32 { | |
| 50 | + ctx_error_occurred = true; | |
| 51 | + 0 | |
| 52 | +} | |
| 53 | + | |
| 54 | + | |
| 55 | +unsafe fn check_gl_error() { | |
| 56 | + let err = gl::GetError(); | |
| 57 | + if err != gl::NO_ERROR { | |
| 58 | + println!("got gl error {}", err); | |
| 59 | + } | |
| 60 | +} | |
| 61 | + | |
| 62 | +// returns the glx version in a decimal form | |
| 63 | +// eg. 1.3 => 13 | |
| 64 | +fn glx_dec_version(dpy: *mut xlib::Display) -> i32 { | |
| 65 | + let mut maj: c_int = 0; | |
| 66 | + let mut min: c_int = 0; | |
| 67 | + unsafe { | |
| 68 | + if glXQueryVersion(dpy, | |
| 69 | + &mut maj as *mut c_int, | |
| 70 | + &mut min as *mut c_int) == 0 { | |
| 71 | + panic!("cannot get glx version"); | |
| 72 | + } | |
| 73 | + } | |
| 74 | + (maj*10 + min) as i32 | |
| 75 | +} | |
| 76 | + | |
| 77 | + | |
| 78 | +fn get_glxfbconfig(dpy: *mut xlib::Display, screen_num: i32, | |
| 79 | + visual_attribs: &[i32]) -> GLXFBConfig { | |
| 80 | + unsafe { | |
| 81 | + let mut fbcount: c_int = 0; | |
| 82 | + let fbcs = glXChooseFBConfig(dpy, screen_num, | |
| 83 | + visual_attribs.as_ptr(), | |
| 84 | + &mut fbcount as *mut c_int); | |
| 85 | + | |
| 86 | + if fbcount == 0 { | |
| 87 | + panic!("could not find compatible fb config"); | |
| 88 | + } | |
| 89 | + // we pick the first from the list | |
| 90 | + let fbc = *fbcs; | |
| 91 | + xlib::XFree(fbcs as *mut c_void); | |
| 92 | + fbc | |
| 93 | + } | |
| 94 | +} | |
| 95 | + | |
| 96 | + | |
| 97 | +fn main() { unsafe { | |
| 98 | + let (conn, screen_num) = xcb::Connection::connect_with_xlib_display().unwrap(); | |
| 99 | + conn.set_event_queue_owner(xcb::EventQueueOwner::Xcb); | |
| 100 | + | |
| 101 | + if glx_dec_version(conn.get_raw_dpy()) < 13 { | |
| 102 | + panic!("glx-1.3 is not supported"); | |
| 103 | + } | |
| 104 | + | |
| 105 | + let fbc = get_glxfbconfig(conn.get_raw_dpy(), screen_num, &[ | |
| 106 | + GLX_X_RENDERABLE , 1, | |
| 107 | + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, | |
| 108 | + GLX_RENDER_TYPE , GLX_RGBA_BIT, | |
| 109 | + GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, | |
| 110 | + GLX_RED_SIZE , 8, | |
| 111 | + GLX_GREEN_SIZE , 8, | |
| 112 | + GLX_BLUE_SIZE , 8, | |
| 113 | + GLX_ALPHA_SIZE , 8, | |
| 114 | + GLX_DEPTH_SIZE , 24, | |
| 115 | + GLX_STENCIL_SIZE , 8, | |
| 116 | + GLX_DOUBLEBUFFER , 1, | |
| 117 | + 0 | |
| 118 | + ]); | |
| 119 | + | |
| 120 | + let vi: *const xlib::XVisualInfo = | |
| 121 | + glXGetVisualFromFBConfig(conn.get_raw_dpy(), fbc); | |
| 122 | + | |
| 123 | + let dri2_ev = { | |
| 124 | + conn.prefetch_extension_data(dri2::id()); | |
| 125 | + match conn.get_extension_data(dri2::id()) { | |
| 126 | + None => { panic!("could not load dri2 extension") }, | |
| 127 | + Some(r) => { r.first_event() } | |
| 128 | + } | |
| 129 | + }; | |
| 130 | + | |
| 131 | + let (wm_protocols, wm_delete_window) = { | |
| 132 | + let pc = xcb::intern_atom(&conn, false, "WM_PROTOCOLS"); | |
| 133 | + let dwc = xcb::intern_atom(&conn, false, "WM_DELETE_WINDOW"); | |
| 134 | + | |
| 135 | + let p = match pc.get_reply() { | |
| 136 | + Ok(p) => p.atom(), | |
| 137 | + Err(_) => panic!("could not load WM_PROTOCOLS atom") | |
| 138 | + }; | |
| 139 | + let dw = match dwc.get_reply() { | |
| 140 | + Ok(dw) => dw.atom(), | |
| 141 | + Err(_) => panic!("could not load WM_DELETE_WINDOW atom") | |
| 142 | + }; | |
| 143 | + (p, dw) | |
| 144 | + }; | |
| 145 | + | |
| 146 | + let setup = conn.get_setup(); | |
| 147 | + let screen = setup.roots().nth((*vi).screen as usize).unwrap(); | |
| 148 | + | |
| 149 | + let cmap = conn.generate_id(); | |
| 150 | + let win = conn.generate_id(); | |
| 151 | + | |
| 152 | + xcb::create_colormap(&conn, xcb::COLORMAP_ALLOC_NONE as u8, | |
| 153 | + cmap, screen.root(), (*vi).visualid as u32); | |
| 154 | + | |
| 155 | + let cw_values = [ | |
| 156 | + (xcb::CW_BACK_PIXEL, screen.white_pixel()), | |
| 157 | + (xcb::CW_BORDER_PIXEL, screen.black_pixel()), | |
| 158 | + (xcb::CW_EVENT_MASK, | |
| 159 | + xcb::EVENT_MASK_KEY_PRESS | xcb::EVENT_MASK_EXPOSURE), | |
| 160 | + (xcb::CW_COLORMAP, cmap) | |
| 161 | + ]; | |
| 162 | + | |
| 163 | + xcb::create_window(&conn, (*vi).depth as u8, win, screen.root(), 0, 0, 640, 480, | |
| 164 | + 0, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16, | |
| 165 | + (*vi).visualid as u32, &cw_values); | |
| 166 | + | |
| 167 | + xlib::XFree(vi as *mut c_void); | |
| 168 | + | |
| 169 | + let title = "XCB OpenGL"; | |
| 170 | + xcb::change_property(&conn, | |
| 171 | + xcb::PROP_MODE_REPLACE as u8, | |
| 172 | + win, | |
| 173 | + xcb::ATOM_WM_NAME, | |
| 174 | + xcb::ATOM_STRING, | |
| 175 | + 8, title.as_bytes()); | |
| 176 | + | |
| 177 | + let protocols = [wm_delete_window]; | |
| 178 | + xcb::change_property(&conn, xcb::PROP_MODE_REPLACE as u8, | |
| 179 | + win, wm_protocols, xcb::ATOM_ATOM, 32, &protocols); | |
| 180 | + | |
| 181 | + xcb::map_window(&conn, win); | |
| 182 | + conn.flush(); | |
| 183 | + xlib::XSync(conn.get_raw_dpy(), xlib::False); | |
| 184 | + | |
| 185 | + let glx_exts = CStr::from_ptr( | |
| 186 | + glXQueryExtensionsString(conn.get_raw_dpy(), screen_num)) | |
| 187 | + .to_str().unwrap(); | |
| 188 | + | |
| 189 | + if !check_glx_extension(&glx_exts, "GLX_ARB_create_context") { | |
| 190 | + panic!("could not find GLX extension GLX_ARB_create_context"); | |
| 191 | + } | |
| 192 | + | |
| 193 | + // with glx, no need of a current context is needed to load symbols | |
| 194 | + // otherwise we would need to create a temporary legacy GL context | |
| 195 | + // for loading symbols (at least glXCreateContextAttribsARB) | |
| 196 | + let glx_create_context_attribs: GlXCreateContextAttribsARBProc = | |
| 197 | + std::mem::transmute(load_gl_func("glXCreateContextAttribsARB")); | |
| 198 | + | |
| 199 | + // loading all other symbols | |
| 200 | + gl::load_with(|n| load_gl_func(&n)); | |
| 201 | + | |
| 202 | + if !gl::GenVertexArrays::is_loaded() { | |
| 203 | + panic!("no GL3 support available!"); | |
| 204 | + } | |
| 205 | + | |
| 206 | + // installing an event handler to check if error is generated | |
| 207 | + ctx_error_occurred = false; | |
| 208 | + let old_handler = xlib::XSetErrorHandler(Some(ctx_error_handler)); | |
| 209 | + | |
| 210 | + let context_attribs: [c_int; 5] = [ | |
| 211 | + GLX_CONTEXT_MAJOR_VERSION_ARB as c_int, 3, | |
| 212 | + GLX_CONTEXT_MINOR_VERSION_ARB as c_int, 0, | |
| 213 | + 0 | |
| 214 | + ]; | |
| 215 | + let ctx = glx_create_context_attribs(conn.get_raw_dpy(), fbc, null_mut(), | |
| 216 | + xlib::True, &context_attribs[0] as *const c_int); | |
| 217 | + | |
| 218 | + conn.flush(); | |
| 219 | + xlib::XSync(conn.get_raw_dpy(), xlib::False); | |
| 220 | + xlib::XSetErrorHandler(std::mem::transmute(old_handler)); | |
| 221 | + | |
| 222 | + if ctx.is_null() || ctx_error_occurred { | |
| 223 | + panic!("error when creating gl-3.0 context"); | |
| 224 | + } | |
| 225 | + | |
| 226 | + if glXIsDirect(conn.get_raw_dpy(), ctx) == 0 { | |
| 227 | + panic!("obtained indirect rendering context") | |
| 228 | + } | |
| 229 | + | |
| 230 | + loop { | |
| 231 | + if let Some(ev) = conn.wait_for_event() { | |
| 232 | + let ev_type = ev.response_type() & !0x80; | |
| 233 | + match ev_type { | |
| 234 | + xcb::EXPOSE => { | |
| 235 | + glXMakeCurrent(conn.get_raw_dpy(), win as xlib::XID, ctx); | |
| 236 | + gl::ClearColor(0.5f32, 0.5f32, 1.0f32, 1.0f32); | |
| 237 | + gl::Clear(gl::COLOR_BUFFER_BIT); | |
| 238 | + gl::Flush(); | |
| 239 | + check_gl_error(); | |
| 240 | + glXSwapBuffers(conn.get_raw_dpy(), win as xlib::XID); | |
| 241 | + glXMakeCurrent(conn.get_raw_dpy(), 0, null_mut()); | |
| 242 | + }, | |
| 243 | + xcb::KEY_PRESS => { | |
| 244 | + break; | |
| 245 | + }, | |
| 246 | + xcb::CLIENT_MESSAGE => { | |
| 247 | + let cmev = unsafe { | |
| 248 | + xcb::cast_event::<xcb::ClientMessageEvent>(&ev) | |
| 249 | + }; | |
| 250 | + if cmev.type_() == wm_protocols && cmev.format() == 32 { | |
| 251 | + let protocol = cmev.data().data32()[0]; | |
| 252 | + if protocol == wm_delete_window { | |
| 253 | + break; | |
| 254 | + } | |
| 255 | + } | |
| 256 | + }, | |
| 257 | + _ => { | |
| 258 | + // following stuff is not obvious at all, but is necessary | |
| 259 | + // to handle GL when XCB owns the event queue | |
| 260 | + if ev_type == dri2_ev || ev_type == dri2_ev+1 { | |
| 261 | + // these are libgl dri2 event that need special handling | |
| 262 | + // see https://bugs.freedesktop.org/show_bug.cgi?id=35945#c4 | |
| 263 | + // and mailing thread starting here: | |
| 264 | + // http://lists.freedesktop.org/archives/xcb/2015-November/010556.html | |
| 265 | + | |
| 266 | + if let Some(proc_) = | |
| 267 | + xlib::XESetWireToEvent(conn.get_raw_dpy(), | |
| 268 | + ev_type as i32, None) { | |
| 269 | + xlib::XESetWireToEvent(conn.get_raw_dpy(), | |
| 270 | + ev_type as i32, Some(proc_)); | |
| 271 | + let raw_ev = ev.ptr; | |
| 272 | + (*raw_ev).sequence = | |
| 273 | + xlib::XLastKnownRequestProcessed( | |
| 274 | + conn.get_raw_dpy()) as u16; | |
| 275 | + let mut dummy: xlib::XEvent = std::mem::zeroed(); | |
| 276 | + proc_(conn.get_raw_dpy(), | |
| 277 | + &mut dummy as *mut xlib::XEvent, | |
| 278 | + raw_ev as *mut xlib::xEvent); | |
| 279 | + } | |
| 280 | + | |
| 281 | + } | |
| 282 | + } | |
| 283 | + } | |
| 284 | + conn.flush(); | |
| 285 | + } | |
| 286 | + else { | |
| 287 | + break; | |
| 288 | + } | |
| 289 | + } | |
| 290 | + | |
| 291 | + // only to make sure that rs_client generate correct names for DRI2 | |
| 292 | + // (used to be "*_DRI_2_*") | |
| 293 | + // should be in a "compile tests" section instead of example | |
| 294 | + let _ = xcb::ffi::dri2::XCB_DRI2_ATTACHMENT_BUFFER_ACCUM; | |
| 295 | + | |
| 296 | + glXDestroyContext(conn.get_raw_dpy(), ctx); | |
| 297 | + | |
| 298 | + xcb::unmap_window(&conn, win); | |
| 299 | + xcb::destroy_window(&conn, win); | |
| 300 | + xcb::free_colormap(&conn, cmap); | |
| 301 | + conn.flush(); | |
| 302 | +}} | ... | ... |
xcb-test/src/main.rs
0 → 100644
| 1 | +extern crate xcb; | |
| 2 | + | |
| 3 | +use std::iter::{Iterator}; | |
| 4 | +use std::{thread, time}; | |
| 5 | +use std::sync::Arc; | |
| 6 | +use std::ptr::{null, null_mut}; | |
| 7 | +use std::slice::from_raw_parts_mut; | |
| 8 | + | |
| 9 | +pub fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) { | |
| 10 | + unsafe { | |
| 11 | + let id = libc::shmget( libc::IPC_PRIVATE | |
| 12 | + , size * 4 | |
| 13 | + , libc::IPC_CREAT | 0o744 ); | |
| 14 | + let ptr = libc::shmat(id, null(), 0); | |
| 15 | + (id as i32, from_raw_parts_mut(ptr as *mut u32, size)) | |
| 16 | + } | |
| 17 | +} | |
| 18 | + | |
| 19 | +fn main() { | |
| 20 | + let points: &[xcb::Point] = &[ xcb::Point::new(10, 10) | |
| 21 | + , xcb::Point::new(10, 20) | |
| 22 | + , xcb::Point::new(20, 10) | |
| 23 | + , xcb::Point::new(20, 20) ]; | |
| 24 | + let polyline: &[xcb::Point] = &[ xcb::Point::new(50, 10 ) | |
| 25 | + // rest of points are relative | |
| 26 | + , xcb::Point::new( 5, 20 ) | |
| 27 | + , xcb::Point::new(25, -20) | |
| 28 | + , xcb::Point::new(10, 10 ) ]; | |
| 29 | + let segments: &[xcb::Segment] = &[ xcb::Segment::new(100, 10, 140, 30) | |
| 30 | + , xcb::Segment::new(110, 25, 130, 60) ]; | |
| 31 | + let rectangles: &[xcb::Rectangle] | |
| 32 | + = &[ xcb::Rectangle::new(10, 50, 40, 20) | |
| 33 | + , xcb::Rectangle::new(80, 50, 10, 40) ]; | |
| 34 | + let arcs: &[xcb::Arc] = &[ xcb::Arc::new(10, 100, 60, 40, 0, 90 << 6) | |
| 35 | + , xcb::Arc::new(90, 100, 55, 40, 0, 270 << 6) ]; | |
| 36 | + | |
| 37 | + let (conn, screen_num) = { | |
| 38 | + let (conn, screen_num) = xcb::Connection::connect(None).unwrap(); | |
| 39 | + (Arc::new(conn), screen_num) | |
| 40 | + }; | |
| 41 | + let setup = conn.get_setup(); | |
| 42 | + let screen = setup.roots().nth(screen_num as usize).unwrap(); | |
| 43 | + | |
| 44 | + let (shmid, shm) = getshm(150 * 150); | |
| 45 | + let shmseg = conn.generate_id(); | |
| 46 | + xcb::shm::attach(&conn, shmseg, shmid as u32, false); | |
| 47 | + unsafe { libc::shmctl(shmid, libc::IPC_RMID, null_mut()); } | |
| 48 | + | |
| 49 | + let foreground = conn.generate_id(); | |
| 50 | + let pix = conn.generate_id(); | |
| 51 | + | |
| 52 | + xcb::create_gc( &conn | |
| 53 | + , foreground | |
| 54 | + , screen.root() | |
| 55 | + , &[ (xcb::GC_FOREGROUND, screen.black_pixel()) | |
| 56 | + , (xcb::GC_GRAPHICS_EXPOSURES, 0) ] ); | |
| 57 | + | |
| 58 | + let window = conn.generate_id(); | |
| 59 | + let values = [ ( xcb::CW_BACK_PIXEL, screen.white_pixel() ) | |
| 60 | + , ( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | |
| 61 | + | xcb::EVENT_MASK_KEY_PRESS | |
| 62 | + | xcb::EVENT_MASK_STRUCTURE_NOTIFY | |
| 63 | + | xcb::EVENT_MASK_PROPERTY_CHANGE ) ]; | |
| 64 | + xcb::create_window( &conn | |
| 65 | + , xcb::COPY_FROM_PARENT as u8 | |
| 66 | + , window | |
| 67 | + , screen.root() | |
| 68 | + , 0, 0, 150, 150, 0 | |
| 69 | + , xcb::WINDOW_CLASS_INPUT_OUTPUT as u16 | |
| 70 | + , screen.root_visual() | |
| 71 | + , &values); | |
| 72 | + | |
| 73 | + xcb::shm::create_pixmap( &conn | |
| 74 | + , pix | |
| 75 | + , window | |
| 76 | + , 150, 150 | |
| 77 | + , screen.root_depth() | |
| 78 | + , shmseg | |
| 79 | + , 0 ); | |
| 80 | + | |
| 81 | + xcb::map_window(&conn, window); | |
| 82 | + | |
| 83 | + { | |
| 84 | + let conn = conn.clone(); | |
| 85 | + | |
| 86 | + thread::spawn(move || { | |
| 87 | + let mut blink = false; | |
| 88 | + | |
| 89 | + let mut i = 0; | |
| 90 | + loop { | |
| 91 | + let title = if blink { | |
| 92 | + "Basic Threaded Window ;-)" | |
| 93 | + } else { | |
| 94 | + "Basic Threaded Window :-)" | |
| 95 | + }; | |
| 96 | + | |
| 97 | + shm[i] = 0xFFFFFF; | |
| 98 | + i = (i + 1) % (150 * 150); | |
| 99 | + | |
| 100 | + let c = xcb::change_property_checked( | |
| 101 | + &conn | |
| 102 | + , xcb::PROP_MODE_REPLACE as u8 | |
| 103 | + , window | |
| 104 | + , xcb::ATOM_WM_NAME | |
| 105 | + , xcb::ATOM_STRING | |
| 106 | + , 8 | |
| 107 | + , title.as_bytes() ); | |
| 108 | + | |
| 109 | + xcb::copy_area( &conn | |
| 110 | + , pix | |
| 111 | + , window | |
| 112 | + , foreground | |
| 113 | + , 0, 0, 0, 0 | |
| 114 | + , 150, 150); | |
| 115 | + | |
| 116 | + if conn.has_error().is_err() || c.request_check().is_err() { | |
| 117 | + break; | |
| 118 | + } | |
| 119 | + | |
| 120 | + blink = !blink; | |
| 121 | + thread::sleep(time::Duration::from_millis(500)); | |
| 122 | + } | |
| 123 | + }); | |
| 124 | + } | |
| 125 | + | |
| 126 | + conn.flush(); | |
| 127 | + | |
| 128 | + loop { | |
| 129 | + let event = conn.wait_for_event(); | |
| 130 | + | |
| 131 | + match event { | |
| 132 | + None => break, | |
| 133 | + Some(event) => { | |
| 134 | + match event.response_type() & !0x80 { | |
| 135 | + xcb::PROPERTY_NOTIFY => { | |
| 136 | + let prop_notify: &xcb::PropertyNotifyEvent = unsafe { | |
| 137 | + xcb::cast_event(&event) | |
| 138 | + }; | |
| 139 | + | |
| 140 | + if prop_notify.atom() == xcb::ATOM_WM_NAME { | |
| 141 | + // retrieving title | |
| 142 | + let cookie = xcb::get_property( &conn | |
| 143 | + , false | |
| 144 | + , window | |
| 145 | + , xcb::ATOM_WM_NAME | |
| 146 | + , xcb::ATOM_STRING | |
| 147 | + , 0, 1024); | |
| 148 | + | |
| 149 | + if let Ok(reply) = cookie.get_reply() { | |
| 150 | + let r = reply.value(); | |
| 151 | + let r = std::str::from_utf8(r).unwrap(); | |
| 152 | + | |
| 153 | + println!( "title changed to \"{}\"", r); | |
| 154 | + } | |
| 155 | + } | |
| 156 | + }, | |
| 157 | + | |
| 158 | + xcb::EXPOSE => { | |
| 159 | + xcb::poly_point( &conn | |
| 160 | + , xcb::COORD_MODE_ORIGIN as u8 | |
| 161 | + , window | |
| 162 | + , foreground | |
| 163 | + , &points ); | |
| 164 | + xcb::poly_line( &conn | |
| 165 | + , xcb::COORD_MODE_PREVIOUS as u8 | |
| 166 | + , window | |
| 167 | + , foreground | |
| 168 | + , &polyline ); | |
| 169 | + xcb::poly_segment( &conn | |
| 170 | + , window | |
| 171 | + , foreground | |
| 172 | + , &segments ); | |
| 173 | + xcb::poly_rectangle( &conn | |
| 174 | + , window | |
| 175 | + , foreground | |
| 176 | + , &rectangles ); | |
| 177 | + xcb::poly_arc( &conn | |
| 178 | + , window | |
| 179 | + , foreground | |
| 180 | + , &arcs ); | |
| 181 | + | |
| 182 | + conn.flush(); | |
| 183 | + }, | |
| 184 | + | |
| 185 | + xcb::KEY_PRESS => { | |
| 186 | + let key_press: &xcb::KeyPressEvent = unsafe { | |
| 187 | + xcb::cast_event(&event) | |
| 188 | + }; | |
| 189 | + | |
| 190 | + println!("Key '{}' pressed", key_press.detail()); | |
| 191 | + | |
| 192 | + if key_press.detail() == 0x18 { // Q (on qwerty) | |
| 193 | + break; | |
| 194 | + } | |
| 195 | + }, | |
| 196 | + | |
| 197 | + _ => {}, | |
| 198 | + } | |
| 199 | + } | |
| 200 | + } | |
| 201 | + } | |
| 202 | +} | ... | ... |
Please
register
or
login
to post a comment