Commit f06b625998d4e914e63ec854a24b47e52357cd37

Authored by Georg Hopp
1 parent 45d1af9c

Add first simple triable animation based on fractionals

... ... @@ -6,3 +6,7 @@ edition = "2018"
6 6
7 7 [dependencies]
8 8 lazy_static = "1.4.0"
  9 +libc = "0.2"
  10 +gl = "0.5.2"
  11 +x11 = { version = "2.3", features = ["glx"] }
  12 +xcb = { version = "0.8", features = ["dri2", "randr", "thread", "xlib_xcb", "shm"] }
... ...
... ... @@ -20,13 +20,19 @@
20 20 //
21 21 use std::cmp;
22 22 use std::fmt::{Formatter, Display, Result};
  23 +use std::sync::mpsc;
23 24
24 25 pub trait Easel {
25   - fn canvas(self) -> dyn Canvas;
  26 + //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>;
26 27 }
27 28
28 29 pub trait Canvas {
29   - fn draw(self, c :&dyn Drawable, ofs :Coordinate);
  30 + fn init_events(&self);
  31 + fn start_events(&self, tx :mpsc::Sender<i32>);
  32 +
  33 + fn clear(&mut self);
  34 + fn draw(&mut self, c :&dyn Drawable, ofs :Coordinate);
  35 + fn show(&self);
30 36 }
31 37
32 38 pub trait Drawable {
... ...
... ... @@ -28,6 +28,7 @@ pub mod fractional;
28 28 pub mod transform;
29 29 pub mod trigonometry;
30 30 pub mod vector;
  31 +pub mod xcb;
31 32
32 33 use fractional::Fractional;
33 34 use vector::Vector;
... ...
... ... @@ -23,6 +23,9 @@ use std::f64::consts::PI as FPI;
23 23 use std::fmt::Display;
24 24 use std::num::TryFromIntError;
25 25 use std::ops::{Add,Sub,Neg,Mul,Div};
  26 +use std::sync::mpsc;
  27 +use std::time;
  28 +use std::thread;
26 29
27 30 use fractional::continuous::Continuous;
28 31 use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline
... ... @@ -32,6 +35,9 @@ use fractional::trigonometry::Trig;
32 35 use fractional::vector::{Vector};
33 36 use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, rotate_v};
34 37
  38 +use fractional::xcb::XcbEasel;
  39 +use fractional::easel::Canvas;
  40 +
35 41 fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> {
36 42 let r = v.iter().fold(0, |acc, x| acc + x);
37 43 let l = i64::try_from(v.len())?;
... ... @@ -325,4 +331,54 @@ fn main() {
325 331 _transform2();
326 332 println!();
327 333 _line();
  334 +
  335 + let xcb = XcbEasel::new().unwrap();
  336 + let mut canvas = xcb.canvas(151, 151).unwrap();
  337 +
  338 + canvas.set_title("Something...");
  339 + canvas.init_events();
  340 +
  341 + let (tx, rx) = mpsc::channel();
  342 + let tx1 = mpsc::Sender::clone(&tx);
  343 + canvas.start_events(tx);
  344 +
  345 + let i = Vector(Fractional( 0,1), Fractional(-35,1), Fractional(0,1));
  346 + let j = Vector(Fractional( 30,1), Fractional( 17,1), Fractional(0,1));
  347 + let k = Vector(Fractional(-30,1), Fractional( 17,1), Fractional(0,1));
  348 +
  349 + fn to_i32(x :Fractional) -> i32 {
  350 + let Fractional(n, d) = x;
  351 + (n / d + if (n % d).abs() < (n / 2).abs() { 0 } else { 1 }) as i32
  352 + }
  353 +
  354 + thread::spawn(move || {
  355 + loop {
  356 + tx1.send(0).unwrap();
  357 + thread::sleep(time::Duration::from_millis(10));
  358 + }
  359 + });
  360 +
  361 + let mut deg :i32 = 0;
  362 +
  363 + for x in rx {
  364 + match x {
  365 + 1 => break,
  366 + _ => {
  367 + let rot :TMatrix<Fractional> = rotate_z(deg);
  368 + let Vector(ix, iy, _) = rot.apply(&i);
  369 + let Vector(jx, jy, _) = rot.apply(&j);
  370 + let Vector(kx, ky, _) = rot.apply(&k);
  371 +
  372 + let pg = Polygon(
  373 + Coordinates(vec!( Coordinate(to_i32(ix), to_i32(iy))
  374 + , Coordinate(to_i32(jx), to_i32(jy))
  375 + , Coordinate(to_i32(kx), to_i32(ky)) )));
  376 +
  377 + canvas.clear();
  378 + canvas.draw(&pg, Coordinate(75,75));
  379 + canvas.show();
  380 + deg = (deg + 3) % 359;
  381 + },
  382 + }
  383 + }
328 384 }
... ...
  1 +//
  2 +// XCB implementation for drawing some stuff...
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2019 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +extern crate xcb;
  22 +
  23 +use std::sync::Arc;
  24 +use std::ptr; //::{null, null_mut};
  25 +use std::thread;
  26 +use std::sync::mpsc;
  27 +
  28 +use crate::easel::{Easel, Canvas, Drawable, Coordinate, Coordinates};
  29 +
  30 +#[derive(Clone)]
  31 +pub struct XcbEasel (Arc<xcb::Connection>, i32);
  32 +
  33 +pub struct XcbCanvas<'a> { conn :Arc<xcb::Connection>
  34 + , width :u16
  35 + , height :u16
  36 + , window :u32
  37 + , pixmap :u32
  38 + , gc :u32
  39 + , shm :&'a mut [u32] }
  40 +
  41 +impl XcbEasel {
  42 + pub fn new() -> Result<XcbEasel, xcb::ConnError> {
  43 + let (conn, num) = xcb::Connection::connect(None)?;
  44 +
  45 + Ok(XcbEasel(Arc::new(conn), num))
  46 + }
  47 +
  48 + pub fn setup(&self) -> xcb::Setup {
  49 + let XcbEasel(conn, _) = self;
  50 + conn.get_setup()
  51 + }
  52 +
  53 + pub fn screen(&self) -> Option<xcb::Screen> {
  54 + let XcbEasel(_, num) = self;
  55 + self.setup().roots().nth(*num as usize)
  56 + }
  57 +
  58 + pub fn canvas(&self, width :u16, height :u16) -> Option<XcbCanvas> {
  59 + let Self(conn, _) = self;
  60 + let conn = conn.clone();
  61 + let screen = match self.screen() {
  62 + None => return None,
  63 + Some(screen) => screen,
  64 + };
  65 +
  66 + let shmseg = conn.generate_id();
  67 + let gc = conn.generate_id();
  68 + let pixmap = conn.generate_id();
  69 + let window = conn.generate_id();
  70 +
  71 + xcb::create_window( &conn, xcb::COPY_FROM_PARENT as u8, window
  72 + , screen.root(), 0, 0, width, width, 0
  73 + , xcb::WINDOW_CLASS_INPUT_OUTPUT as u16
  74 + , screen.root_visual()
  75 + , &[(xcb::CW_BACK_PIXEL, screen.white_pixel())] );
  76 +
  77 + xcb::create_gc( &conn, gc, screen.root()
  78 + , &[ (xcb::GC_FOREGROUND, screen.black_pixel())
  79 + , (xcb::GC_GRAPHICS_EXPOSURES, 0) ] );
  80 +
  81 + let (shmid, shm) = getshm((width * height) as usize);
  82 + xcb::shm::attach(&conn, shmseg, shmid as u32, false);
  83 + unsafe { libc::shmctl(shmid, libc::IPC_RMID, ptr::null_mut()); }
  84 +
  85 + xcb::shm::create_pixmap( &conn, pixmap, window, width, height
  86 + , screen.root_depth(), shmseg, 0 );
  87 +
  88 + xcb::map_window(&conn, window);
  89 + conn.flush();
  90 +
  91 + Some(XcbCanvas{ conn: conn
  92 + , width: width
  93 + , height: height
  94 + , window: window
  95 + , pixmap: pixmap
  96 + , gc: gc
  97 + , shm: shm } )
  98 + }
  99 +}
  100 +
  101 +impl<'a> XcbCanvas<'a> {
  102 + pub fn set_title(&self, title :&str) {
  103 + let c = xcb::change_property_checked( &self.conn
  104 + , xcb::PROP_MODE_REPLACE as u8
  105 + , self.window
  106 + , xcb::ATOM_WM_NAME
  107 + , xcb::ATOM_STRING
  108 + , 8
  109 + , title.as_bytes() );
  110 + if self.conn.has_error().is_err() || c.request_check().is_err() {
  111 + println!("Error setting title");
  112 + }
  113 + }
  114 +}
  115 +
  116 +fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) {
  117 + use std::slice::from_raw_parts_mut;
  118 +
  119 + unsafe {
  120 + let id = libc::shmget( libc::IPC_PRIVATE
  121 + , size * 4
  122 + , libc::IPC_CREAT | 0o744 );
  123 + let ptr = libc::shmat(id, ptr::null(), 0);
  124 + (id as i32, from_raw_parts_mut(ptr as *mut u32, size))
  125 + }
  126 +}
  127 +
  128 +impl Easel for XcbEasel {}
  129 +
  130 +impl<'a> Canvas for XcbCanvas<'a> {
  131 + fn init_events(&self) {
  132 + let mask = [( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE
  133 + | xcb::EVENT_MASK_KEY_PRESS
  134 + | xcb::EVENT_MASK_STRUCTURE_NOTIFY
  135 + | xcb::EVENT_MASK_PROPERTY_CHANGE )];
  136 + xcb::change_window_attributes(&self.conn, self.window, &mask);
  137 + self.conn.flush();
  138 + }
  139 +
  140 + fn start_events(&self, tx :mpsc::Sender<i32>) {
  141 + let conn = self.conn.clone();
  142 + let window = self.window;
  143 + let pixmap = self.pixmap;
  144 + let gc = self.gc;
  145 + let width = self.width;
  146 + let height = self.height;
  147 +
  148 + thread::spawn(move || {
  149 + loop {
  150 + let event = conn.wait_for_event();
  151 +
  152 + match event {
  153 + None => break,
  154 + Some(event) => {
  155 + match event.response_type() & !0x80 {
  156 + xcb::PROPERTY_NOTIFY => {
  157 + let prop_notify :&xcb::PropertyNotifyEvent
  158 + = unsafe { xcb::cast_event(&event) };
  159 +
  160 + if prop_notify.atom() == xcb::ATOM_WM_NAME {
  161 + // retrieving title
  162 + let cookie
  163 + = xcb::get_property( &conn
  164 + , false
  165 + , window
  166 + , xcb::ATOM_WM_NAME
  167 + , xcb::ATOM_STRING
  168 + , 0, 1024 );
  169 +
  170 + if let Ok(reply) = cookie.get_reply() {
  171 + let r = reply.value();
  172 + let r = std::str::from_utf8(r).unwrap();
  173 +
  174 + println!("title changed to: {}", r);
  175 + }
  176 + }
  177 + },
  178 +
  179 + xcb::EXPOSE => {
  180 + xcb::copy_area( &conn, pixmap, window, gc
  181 + , 0, 0, 0, 0
  182 + , width, height );
  183 + conn.flush();
  184 + },
  185 +
  186 + xcb::KEY_PRESS => {
  187 + let key_press: &xcb::KeyPressEvent
  188 + = unsafe { xcb::cast_event(&event) };
  189 +
  190 + println!( "Key '{}' pressed"
  191 + , key_press.detail() );
  192 +
  193 + // Q (on qwerty)
  194 + if key_press.detail() == 0x18 {
  195 + tx.send(1).unwrap();
  196 + break;
  197 + }
  198 + },
  199 +
  200 + _ => {},
  201 + }
  202 + },
  203 + }
  204 + }
  205 + });
  206 + }
  207 +
  208 + fn clear(&mut self) {
  209 + unsafe {
  210 + let ptr = self.shm.as_mut_ptr();
  211 + ptr::write_bytes( ptr, 0
  212 + , self.width as usize * self.height as usize);
  213 + }
  214 + }
  215 +
  216 + fn draw(&mut self, d :&dyn Drawable, ofs :Coordinate) {
  217 + let Coordinates(c) = d.plot();
  218 + let Coordinate(xofs, yofs) = ofs;
  219 +
  220 + for Coordinate(x, y) in c {
  221 + let idx :usize = ((y+yofs)*(self.width as i32)+x+xofs) as usize;
  222 + self.shm[idx] = 0xFFFFFF;
  223 + }
  224 + }
  225 +
  226 + fn show(&self) {
  227 + xcb::copy_area( &self.conn, self.pixmap, self.window, self.gc
  228 + , 0, 0, 0, 0
  229 + , self.width, self.height );
  230 + self.conn.flush();
  231 + }
  232 +}
... ...
Please register or login to post a comment