5 changed files with 301 additions and 2 deletions
-
4fractional/Cargo.toml
-
10fractional/src/easel.rs
-
1fractional/src/lib.rs
-
56fractional/src/main.rs
-
232fractional/src/xcb.rs
@ -0,0 +1,232 @@ |
|||||
|
//
|
||||
|
// XCB implementation for drawing some stuff...
|
||||
|
//
|
||||
|
// Georg Hopp <georg@steffers.org>
|
||||
|
//
|
||||
|
// Copyright © 2019 Georg Hopp
|
||||
|
//
|
||||
|
// This program is free software: you can redistribute it and/or modify
|
||||
|
// it under the terms of the GNU General Public License as published by
|
||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||
|
// (at your option) any later version.
|
||||
|
//
|
||||
|
// This program is distributed in the hope that it will be useful,
|
||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
|
// GNU General Public License for more details.
|
||||
|
//
|
||||
|
// You should have received a copy of the GNU General Public License
|
||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
//
|
||||
|
extern crate xcb;
|
||||
|
|
||||
|
use std::sync::Arc;
|
||||
|
use std::ptr; //::{null, null_mut};
|
||||
|
use std::thread;
|
||||
|
use std::sync::mpsc;
|
||||
|
|
||||
|
use crate::easel::{Easel, Canvas, Drawable, Coordinate, Coordinates};
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub struct XcbEasel (Arc<xcb::Connection>, i32);
|
||||
|
|
||||
|
pub struct XcbCanvas<'a> { conn :Arc<xcb::Connection>
|
||||
|
, width :u16 |
||||
|
, height :u16 |
||||
|
, window :u32 |
||||
|
, pixmap :u32 |
||||
|
, gc :u32 |
||||
|
, shm :&'a mut [u32] }
|
||||
|
|
||||
|
impl XcbEasel {
|
||||
|
pub fn new() -> Result<XcbEasel, xcb::ConnError> {
|
||||
|
let (conn, num) = xcb::Connection::connect(None)?;
|
||||
|
|
||||
|
Ok(XcbEasel(Arc::new(conn), num))
|
||||
|
}
|
||||
|
|
||||
|
pub fn setup(&self) -> xcb::Setup {
|
||||
|
let XcbEasel(conn, _) = self;
|
||||
|
conn.get_setup()
|
||||
|
}
|
||||
|
|
||||
|
pub fn screen(&self) -> Option<xcb::Screen> {
|
||||
|
let XcbEasel(_, num) = self;
|
||||
|
self.setup().roots().nth(*num as usize)
|
||||
|
}
|
||||
|
|
||||
|
pub fn canvas(&self, width :u16, height :u16) -> Option<XcbCanvas> {
|
||||
|
let Self(conn, _) = self;
|
||||
|
let conn = conn.clone();
|
||||
|
let screen = match self.screen() {
|
||||
|
None => return None,
|
||||
|
Some(screen) => screen,
|
||||
|
};
|
||||
|
|
||||
|
let shmseg = conn.generate_id();
|
||||
|
let gc = conn.generate_id();
|
||||
|
let pixmap = conn.generate_id();
|
||||
|
let window = conn.generate_id();
|
||||
|
|
||||
|
xcb::create_window( &conn, xcb::COPY_FROM_PARENT as u8, window
|
||||
|
, screen.root(), 0, 0, width, width, 0
|
||||
|
, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16
|
||||
|
, screen.root_visual()
|
||||
|
, &[(xcb::CW_BACK_PIXEL, screen.white_pixel())] );
|
||||
|
|
||||
|
xcb::create_gc( &conn, gc, screen.root()
|
||||
|
, &[ (xcb::GC_FOREGROUND, screen.black_pixel())
|
||||
|
, (xcb::GC_GRAPHICS_EXPOSURES, 0) ] );
|
||||
|
|
||||
|
let (shmid, shm) = getshm((width * height) as usize);
|
||||
|
xcb::shm::attach(&conn, shmseg, shmid as u32, false);
|
||||
|
unsafe { libc::shmctl(shmid, libc::IPC_RMID, ptr::null_mut()); }
|
||||
|
|
||||
|
xcb::shm::create_pixmap( &conn, pixmap, window, width, height
|
||||
|
, screen.root_depth(), shmseg, 0 );
|
||||
|
|
||||
|
xcb::map_window(&conn, window);
|
||||
|
conn.flush();
|
||||
|
|
||||
|
Some(XcbCanvas{ conn: conn
|
||||
|
, width: width
|
||||
|
, height: height
|
||||
|
, window: window
|
||||
|
, pixmap: pixmap
|
||||
|
, gc: gc
|
||||
|
, shm: shm } )
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<'a> XcbCanvas<'a> {
|
||||
|
pub fn set_title(&self, title :&str) {
|
||||
|
let c = xcb::change_property_checked( &self.conn
|
||||
|
, xcb::PROP_MODE_REPLACE as u8
|
||||
|
, self.window
|
||||
|
, xcb::ATOM_WM_NAME
|
||||
|
, xcb::ATOM_STRING
|
||||
|
, 8
|
||||
|
, title.as_bytes() );
|
||||
|
if self.conn.has_error().is_err() || c.request_check().is_err() {
|
||||
|
println!("Error setting title");
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) {
|
||||
|
use std::slice::from_raw_parts_mut;
|
||||
|
|
||||
|
unsafe {
|
||||
|
let id = libc::shmget( libc::IPC_PRIVATE
|
||||
|
, size * 4
|
||||
|
, libc::IPC_CREAT | 0o744 );
|
||||
|
let ptr = libc::shmat(id, ptr::null(), 0);
|
||||
|
(id as i32, from_raw_parts_mut(ptr as *mut u32, size))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl Easel for XcbEasel {}
|
||||
|
|
||||
|
impl<'a> Canvas for XcbCanvas<'a> {
|
||||
|
fn init_events(&self) {
|
||||
|
let mask = [( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE
|
||||
|
| xcb::EVENT_MASK_KEY_PRESS
|
||||
|
| xcb::EVENT_MASK_STRUCTURE_NOTIFY
|
||||
|
| xcb::EVENT_MASK_PROPERTY_CHANGE )];
|
||||
|
xcb::change_window_attributes(&self.conn, self.window, &mask);
|
||||
|
self.conn.flush();
|
||||
|
}
|
||||
|
|
||||
|
fn start_events(&self, tx :mpsc::Sender<i32>) {
|
||||
|
let conn = self.conn.clone();
|
||||
|
let window = self.window;
|
||||
|
let pixmap = self.pixmap;
|
||||
|
let gc = self.gc;
|
||||
|
let width = self.width;
|
||||
|
let height = self.height;
|
||||
|
|
||||
|
thread::spawn(move || {
|
||||
|
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::copy_area( &conn, pixmap, window, gc
|
||||
|
, 0, 0, 0, 0
|
||||
|
, width, height );
|
||||
|
conn.flush();
|
||||
|
},
|
||||
|
|
||||
|
xcb::KEY_PRESS => {
|
||||
|
let key_press: &xcb::KeyPressEvent
|
||||
|
= unsafe { xcb::cast_event(&event) };
|
||||
|
|
||||
|
println!( "Key '{}' pressed"
|
||||
|
, key_press.detail() );
|
||||
|
|
||||
|
// Q (on qwerty)
|
||||
|
if key_press.detail() == 0x18 {
|
||||
|
tx.send(1).unwrap();
|
||||
|
break;
|
||||
|
}
|
||||
|
},
|
||||
|
|
||||
|
_ => {},
|
||||
|
}
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
||||
|
});
|
||||
|
}
|
||||
|
|
||||
|
fn clear(&mut self) {
|
||||
|
unsafe {
|
||||
|
let ptr = self.shm.as_mut_ptr();
|
||||
|
ptr::write_bytes( ptr, 0
|
||||
|
, self.width as usize * self.height as usize);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn draw(&mut self, d :&dyn Drawable, ofs :Coordinate) {
|
||||
|
let Coordinates(c) = d.plot();
|
||||
|
let Coordinate(xofs, yofs) = ofs;
|
||||
|
|
||||
|
for Coordinate(x, y) in c {
|
||||
|
let idx :usize = ((y+yofs)*(self.width as i32)+x+xofs) as usize;
|
||||
|
self.shm[idx] = 0xFFFFFF;
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn show(&self) {
|
||||
|
xcb::copy_area( &self.conn, self.pixmap, self.window, self.gc
|
||||
|
, 0, 0, 0, 0
|
||||
|
, self.width, self.height );
|
||||
|
self.conn.flush();
|
||||
|
}
|
||||
|
}
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue