main.rs 7.39 KB
extern crate xcb;

use std::iter::{Iterator};
use std::{thread, time};
use std::sync::Arc;
use std::ptr::{null, null_mut};
use std::slice::from_raw_parts_mut;

pub fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) {
    unsafe {
        let id = libc::shmget( libc::IPC_PRIVATE
                             , size * 4
                             , libc::IPC_CREAT | 0o744 );
        let ptr = libc::shmat(id, null(), 0);
        (id as i32, from_raw_parts_mut(ptr as *mut u32, size))
    }
}

fn main() {
    let points: &[xcb::Point] = &[ xcb::Point::new(10, 10)
                                 , xcb::Point::new(10, 20)
                                 , xcb::Point::new(20, 10)
                                 , xcb::Point::new(20, 20) ];
    let polyline: &[xcb::Point] = &[ xcb::Point::new(50, 10 )
                                   // rest of points are relative
                                   , xcb::Point::new( 5, 20 )
                                   , xcb::Point::new(25, -20)
                                   , xcb::Point::new(10, 10 ) ];
    let segments: &[xcb::Segment] = &[ xcb::Segment::new(100, 10, 140, 30)
                                     , xcb::Segment::new(110, 25, 130, 60) ];
    let rectangles: &[xcb::Rectangle]
        = &[ xcb::Rectangle::new(10, 50, 40, 20)
           , xcb::Rectangle::new(80, 50, 10, 40) ];
    let arcs: &[xcb::Arc] = &[ xcb::Arc::new(10, 100, 60, 40, 0, 90 << 6)
                             , xcb::Arc::new(90, 100, 55, 40, 0, 270 << 6) ];

    let (conn, screen_num) = {
        let (conn, screen_num) = xcb::Connection::connect(None).unwrap();
        (Arc::new(conn), screen_num)
    };
    let setup = conn.get_setup();
    let screen = setup.roots().nth(screen_num as usize).unwrap();

    let (shmid, shm) = getshm(150 * 150);
    let shmseg       = conn.generate_id();
    xcb::shm::attach(&conn, shmseg, shmid as u32, false);
    unsafe { libc::shmctl(shmid, libc::IPC_RMID, null_mut()); }

    let foreground = conn.generate_id();
    let pix        = conn.generate_id();

    xcb::create_gc( &conn
                  , foreground
                  , screen.root()
                  , &[ (xcb::GC_FOREGROUND, screen.black_pixel())
                     , (xcb::GC_GRAPHICS_EXPOSURES, 0) ] );

    let window = conn.generate_id();
    let values = [ ( xcb::CW_BACK_PIXEL, screen.white_pixel() )
                 , ( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE
                                       | xcb::EVENT_MASK_KEY_PRESS
                                       | xcb::EVENT_MASK_STRUCTURE_NOTIFY
                                       | xcb::EVENT_MASK_PROPERTY_CHANGE ) ];
    xcb::create_window( &conn
                      , xcb::COPY_FROM_PARENT as u8
                      , window
                      , screen.root()
                      , 0, 0, 150, 150, 0
                      , xcb::WINDOW_CLASS_INPUT_OUTPUT as u16
                      , screen.root_visual()
                      , &values);

    xcb::shm::create_pixmap( &conn
                           , pix
                           , window
                           , 150, 150
                           , screen.root_depth()
                           , shmseg
                           , 0 );

    xcb::map_window(&conn, window);

    {
        let conn = conn.clone();

        thread::spawn(move || {
            let mut blink = false;

            let mut i = 0;
            loop {
                let title = if blink {
                    "Basic Threaded Window ;-)"
                } else {
                    "Basic Threaded Window :-)"
                };

                shm[i] = 0xFFFFFF;
                i = (i + 1) % (150 * 150);

                let c = xcb::change_property_checked(
                      &conn
                    , xcb::PROP_MODE_REPLACE as u8
                    , window
                    , xcb::ATOM_WM_NAME
                    , xcb::ATOM_STRING
                    , 8
                    , title.as_bytes() );

                xcb::copy_area( &conn
                              , pix
                              , window
                              , foreground
                              , 0, 0, 0, 0
                              , 150, 150);

                if conn.has_error().is_err() || c.request_check().is_err() {
                    break;
                }

                blink = !blink;
                thread::sleep(time::Duration::from_millis(500));
            }
        });
    }

    conn.flush();

    loop {
        let event = conn.wait_for_event();

        match event {
            None        => break,
            Some(event) => {
                match event.response_type() & !0x80 {
                    xcb::PROPERTY_NOTIFY => {
                        let prop_notify: &xcb::PropertyNotifyEvent = unsafe {
                            xcb::cast_event(&event)
                        };

                        if prop_notify.atom() == xcb::ATOM_WM_NAME {
                            // retrieving title
                            let cookie = xcb::get_property( &conn
                                                          , false
                                                          , window
                                                          , xcb::ATOM_WM_NAME
                                                          , xcb::ATOM_STRING
                                                          , 0, 1024);

                            if let Ok(reply) = cookie.get_reply() {
                                let r = reply.value();
                                let r = std::str::from_utf8(r).unwrap();

                                println!( "title changed to \"{}\"", r);
                            }
                        }
                    },

                    xcb::EXPOSE => {
                        xcb::poly_point( &conn
                                       , xcb::COORD_MODE_ORIGIN as u8
                                       , window
                                       , foreground
                                       , &points );
                        xcb::poly_line( &conn
                                      , xcb::COORD_MODE_PREVIOUS as u8
                                      , window
                                      , foreground
                                      , &polyline );
                        xcb::poly_segment( &conn
                                         , window
                                         , foreground
                                         , &segments );
                        xcb::poly_rectangle( &conn
                                           , window
                                           , foreground
                                           , &rectangles );
                        xcb::poly_arc( &conn
                                     , window
                                     , foreground
                                     , &arcs );

                        conn.flush();
                    },

                    xcb::KEY_PRESS => {
                        let key_press: &xcb::KeyPressEvent = unsafe {
                            xcb::cast_event(&event)
                        };

                        println!("Key '{}' pressed", key_press.detail());

                        if key_press.detail() == 0x18 { // Q (on qwerty)
                            break;
                        }
                    },

                    _ => {},
                }
            }
        }
    }
}