Commit f06b625998d4e914e63ec854a24b47e52357cd37
1 parent
45d1af9c
Add first simple triable animation based on fractionals
Showing
5 changed files
with
301 additions
and
2 deletions
| ... | ... | @@ -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 { | ... | ... |
| ... | ... | @@ -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 | } | ... | ... |
fractional/src/xcb.rs
0 → 100644
| 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