From f845d59213acc60e0c0bd84cd60f3413fbd2446b Mon Sep 17 00:00:00 2001 From: Mohammed Alyousef Date: Sun, 7 Feb 2021 21:36:21 +0300 Subject: [PATCH] add fltk example (#137) * add fltk example * fix clippy lint * fix rustfmt check * add missing trailing new line * use no-pango feature * remove call to win.draw() --- README.md | 1 + examples/minimal-fltk/Cargo.toml | 16 +++++ examples/minimal-fltk/README.md | 15 +++++ examples/minimal-fltk/src/main.rs | 107 ++++++++++++++++++++++++++++++ img/minimal-fltk.png | Bin 0 -> 6880 bytes 5 files changed, 139 insertions(+) create mode 100644 examples/minimal-fltk/Cargo.toml create mode 100644 examples/minimal-fltk/README.md create mode 100644 examples/minimal-fltk/src/main.rs create mode 100644 img/minimal-fltk.png diff --git a/README.md b/README.md index a7df661..a4890ec 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Rapidly prototype a simple 2D game, pixel-based animations, software renderers, - [Dear ImGui example with `winit`](./examples/imgui-winit) - [Minimal example with SDL2](./examples/minimal-sdl2) - [Minimal example with `winit`](./examples/minimal-winit) +- [Minimal example with `fltk`](./examples/minimal-fltk) - [Pixel Invaders](./examples/invaders) - [`raqote` example](./examples/raqote-winit) diff --git a/examples/minimal-fltk/Cargo.toml b/examples/minimal-fltk/Cargo.toml new file mode 100644 index 0000000..1a1a8bd --- /dev/null +++ b/examples/minimal-fltk/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "minimal-fltk" +version = "0.1.0" +authors = ["Jay Oster "] +edition = "2018" +publish = false + +[features] +optimize = ["log/release_max_level_warn"] +default = ["optimize"] + +[dependencies] +fltk = { version = "0.14", features = ["no-pango"] } +env_logger = "0.7.1" +log = "0.4.8" +pixels = { path = "../.." } diff --git a/examples/minimal-fltk/README.md b/examples/minimal-fltk/README.md new file mode 100644 index 0000000..b332073 --- /dev/null +++ b/examples/minimal-fltk/README.md @@ -0,0 +1,15 @@ +# Hello Pixels + +![Hello Pixels](../../img/minimal-fltk.png) + +Minimal example with fltk. + +## Running + +```bash +cargo run --release --package minimal-fltk +``` + +## About + +This example demonstrates the absolute minimum for creating an fltk window and pixel buffer. It animates a purple circle moving on a darker purple background, just for _something_ interesting to display. diff --git a/examples/minimal-fltk/src/main.rs b/examples/minimal-fltk/src/main.rs new file mode 100644 index 0000000..87a4ffa --- /dev/null +++ b/examples/minimal-fltk/src/main.rs @@ -0,0 +1,107 @@ +#![deny(clippy::all)] +#![forbid(unsafe_code)] +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use fltk::{app, prelude::*, window::Window}; +use log::error; +use pixels::{Error, Pixels, SurfaceTexture}; +use std::{thread, time::Duration}; + +const WIDTH: u32 = 600; +const HEIGHT: u32 = 400; +const CIRCLE_RADIUS: i16 = 64; +const SLEEP: u64 = 16; + +/// Representation of the application state. In this example, a circle will bounce around the screen. +struct World { + circle_x: i16, + circle_y: i16, + velocity_x: i16, + velocity_y: i16, +} + +fn main() -> Result<(), Error> { + env_logger::init(); + let app = app::App::default(); + let mut win = Window::default() + .with_size(WIDTH as i32, HEIGHT as i32) + .with_label("Hello Pixels"); + win.end(); + win.make_resizable(true); + win.show(); + + let mut pixels = { + let surface_texture = SurfaceTexture::new(WIDTH, HEIGHT, &win); + Pixels::new(WIDTH, HEIGHT, surface_texture)? + }; + + let mut world = World::new(); + + while app.wait() { + // Update internal state + world.update(); + // Draw the current frame + world.draw(pixels.get_frame()); + if pixels + .render() + .map_err(|e| error!("pixels.render() failed: {}", e)) + .is_err() + { + app.quit(); + } + win.redraw(); + // Calls to redraw in the event loop require an explicit sleep + thread::sleep(Duration::from_millis(SLEEP)); + } + + Ok(()) +} + +impl World { + /// Create a new `World` instance that can draw a moving circle. + fn new() -> Self { + Self { + circle_x: 300, + circle_y: 200, + velocity_x: 5, + velocity_y: 5, + } + } + + /// Update the `World` internal state; bounce the circle around the screen. + fn update(&mut self) { + if self.circle_x - CIRCLE_RADIUS <= 0 || self.circle_x + CIRCLE_RADIUS > WIDTH as i16 { + self.velocity_x *= -1; + } + if self.circle_y - CIRCLE_RADIUS <= 0 || self.circle_y + CIRCLE_RADIUS > HEIGHT as i16 { + self.velocity_y *= -1; + } + + self.circle_x += self.velocity_x; + self.circle_y += self.velocity_y; + } + + /// Draw the `World` state to the frame buffer. + /// + /// Assumes the default texture format: `wgpu::TextureFormat::Rgba8UnormSrgb` + fn draw(&self, frame: &mut [u8]) { + for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { + let x = (i % WIDTH as usize) as i16; + let y = (i / WIDTH as usize) as i16; + let d = { + let xd = x as i32 - self.circle_x as i32; + let yd = y as i32 - self.circle_y as i32; + ((xd.pow(2) + yd.pow(2)) as f64).sqrt().powi(2) + }; + let inside_the_circle = d < (CIRCLE_RADIUS as f64).powi(2); + + let rgba = if inside_the_circle { + [0xac, 0x00, 0xe6, 0xff] + } else { + [0x26, 0x00, 0x33, 0xff] + }; + + pixel.copy_from_slice(&rgba); + } + } +} diff --git a/img/minimal-fltk.png b/img/minimal-fltk.png new file mode 100644 index 0000000000000000000000000000000000000000..0cfdff5311795109422659914635cb4957480050 GIT binary patch literal 6880 zcmeHMcTiL7w%>|?M-Y#K^x_o-5u`;S3IPidDH`bkf;0&T2vVekt{3U#SU?aA9uO4i z0zxPW2!vy(5h+pImG%8r`K|Ts zl&e=v_wJG10|3BYvr8t{0I+ij{3z_+363mA1|A15LZQ}IY``mc1e6Hh2LRbDGm~?+ z5&4T#F6mR@xnu0P77Pi;6;wTP*&ky62-xHwN70D%@>YebRJ0W*X>YAOhUwb6HI_E?$Zr(E=dg1Wb_=nI(n0vdN4Dz6i&M}B$RDW&= zMyKCRexPiiHx36Wz&0+|bbI5tQ1auIxNV}1Q@yX9N!`TaZ_nyB#P`7>c!=%KwQ*bY zonoltH@7>I6kFD3jQI_&+ueyg&0ez`E2@f^W$M_vDxY2Dn$OMcE8q_7qss9lxmq3R z%B`Do1>e6JYpwwz-(RptR0}@M)A+f>@8J$o-19(>eW`6~@_SN-TpI4WrhVYX<8Xhc z(`?2Nf4yz!uBz11ME>>$W1zL=5Mp2x71IOLN5xVZf}3*M6)Sv6(p9Ut%aR-fjm!t+#+~%`U3z2oN?G==w2Dm{<6%`#Wg%O z@OV~#%t^1>)7#)oqYpm;XME_!uZNti`Ro%}=E7p~V}uwUJZ?4D8;56P8k~VHi#psP zu$e=y5uf7>x!%UyAtJY3ivNv8;5zUK99Jb?nrR$VPcMw#neyx3kNfwBOf#Q0@r_|? z4G0C?*GM5HLM^9_AQBr)6DT%NQp}HyUm>FPT=^rkSR8G0DQ4dz^FRfkFa&f|2ADoj z|H6(FDs|@8belr>B)bEgm}Sd*fhbP|A1{Vq_gf6(`K?RBX9(heV4tpGx!ZS*l`9v5NxZXi>+3>T0SJMC5~x&Si4l_#)fB%o|?ZRCt@-`_5B2 zFyB94_U2Qgp);@%*|cQyVyCiU-+CuJO4Dw9l~5C0f7-~1ua5?CTB&fjgI-?^As|mi zKJ{7tWH7+%Bd+?)M#hG0km0HT_r%<2@7vp!y%7mFo(Ih`-~9?%qy)D*mSP|KF|xi# z;NPc)Ob`~WOBMDP2S>qKx#kr*TeX)xK}kGf3=MB_4tsNA$oN8@s8O^xA9oX5PiqMa z^%>RhLr;vHm@}Sy&6|%3H~iKW;<)k!Ruew$x8CoIYfoZL-eyjAm31%W^%$!Ig6e(e zDUh(c@18g|W~Rv4%v!EkmX4H{OILm9E?Z^$RydZPB4@Faed@=>`F;%(GuD1x+EkyjyuK0T1<63SYda;APS12n9L0x6n(E>0!77s!u}FPg`+So_)g z_Lk_#&^eY*P{1xcp~YMUszoiYIXs!gm00L|lIxDwql?GO?Yq_M+2$3F6?#20_RUki zCuK88V`E59|MLA6jj!j(1Si^2%V75}p*ec99# z)j{!$Jdmh~}qYx>g#smk`=@#&5NYVBf8LTk<3(A=AFZ{vaNDa0FLm4=%@eB;=b zxrhS<46~VGJJz*^jW?&916s@Z**$LpoHzROFx1sIYfbiLFh}i&hiCJ2ox|d`x0RkY zI2mx41%^u6or&8+Q64PsxmBb7JYu+qH~KId4H+N^_s6ND2{MAf6#5k}OY$YUgg1RB znL9@O{`4ZFh4)~5@dqyTJu|_j67^0MQvnFY$Ec>iBJQ_PgdK9$JmluR>C&us&4DqIlFUU%|=_xEMt6BPwxtc7QyTbB!bf(W=;sW zwSR6Z@_oX~Qt>Ofn>Jirikts9C)Ud4Z{zJtd$;jU6r+vrgT#&Bh-*O)fVRV9bvK3{ zsSWwo-@A8@L?X>{(qS;z{Aks{K`e0AI#2KBlW#k1@Tgec1dWh01OUCmn+?tYut)Wu zq#@NDHPg-7MO!7v8A zx^4!umTlr)AU=J-Ook~m{L)2A->yiHU9JC}sf zunm>3Y=+Un&Eld=5#YK;$lYB(AW&xl`4r?ZJ zsZ?uZ+ba53t&z)y8U^Zk@Tpk8i;U3xC(+zADEBFs>>&mm@;a0oMQ6wr8uzULUiks4xJFx{Bs<@1;u6_XxVNl%1tb|>f*s{Qsr~8AP7g`J})iC*K^J(?^ol&Z> zC@gg~Xcd{hUBzwRm_G$5sCv1joQZoFet|<1k3(`yLaI*(Q04Wpq~%Nb@)z&;V-K@$q^?3>;vg)?fF;g!%r!twU?$LqFk2w`<$1ypp z?^7vLqb^`^KDCRQIrg(NOQNo>@$=d{EUO>&dHL6;|P(o{87o{+1=j+$% z0*SfMnsoxVH#;JnQY-PLfKE{&(V%M5Il|4lSDzD*E2iv3ze1vXR-w1r`>deFrU>2u z{Y#t&q^-CxmwGU+`Y&%JVLbGnz92mX$;mD10k?aZzu$p-}c_iK}gcfL2k#3BClpt0+*Zjz;<=dI7taFv4an^l-+_q^~6bAR_UXSjt z7=`1HSGq_!h;<_5bR;R77(c=n*P{dM%GV-C1)UV;8%T>MOqSr=BlJWTJ(bH*f{9LenVwOsfitg?XJEA3O-$S@uo$~M23aM&`eN9?H+gh zeO+zgn3^z)N&5^Z%3bZFRr@vcK}r6@MeFw>222?cqiu|uv%)-&vDU$U2+^5LwWlxu zk)9hwZDCNwm_tUzp4kMa4}qx)rk=~I5yjw|6G4ZYeWuv@!(Cpup^J~Lca8ggNK@Q} z6txyY&tx}7fgZ$>=xwhET3@}Uixhe;+DpD!V?+KN?6(k|^jI`|#~<7{*}e;Mt0nN7 z7(cOcPD~{yml3lP%+=BmwiYU3Fvk{J?hLLz7q#@hz==C)#DM0u{66qUW2Id@?>SLX zQ_ECqv|VN5Ax6=WC>ze9wmZM*f{z$zwIM-!6Bet}^touL`XB%LJJTklH6LACvl^TF zJkrR+XdMexRuhm4cV2j;3yzF!i2XkDr)Dleq#~KxEH5^ZksrC+#f;pR2paZrhkNsV zL}7g8k^gl`@P9z{W#!niG~ABtZ2NSgJhAbx9vJ5Ef%B>`DZ~PnAEuir1=~HS1EvFN zR)WZ>A{}YrB!&s)?x6dHAkHOuP0XHW9e>tpB=!?Mvs)cR^?0Q^=I1jEHg}|bEbhz| z(Ax&hMq=H~K^E}~s$;4#bCh+oKMN)X{274SH4&RL2Lsbd%d0gN%yW%LigY$xxnC`U zd^GwcU?PL=po}wONsD5AKB6Rq0!?(v{P+Gi=L$r&BdAcJqEOf!r*f@5+u`S?$1XBC z{qLYZa*oZyOH%};R$EDnv_ebQ%IJKT97d@Ho{pj*)+aDR$oEeu(wX|3ao<~1s99>5 zJ#IVjZMz`SX%GadNG!FFSq$a)FKB_|?q)JM?H{GL3fQz_%ZplFE7n3yxIY^9CAT|{ zrxmL{6wSSAtYn_cspw`n3w!dvKNmyi}M4fQL$M0^mJ^?okG6&V2Ls7dgN+K(p}tc7MtpNJM2aKeX{DMB`v5yA6 z1H!zXJ-k91&#KlJ-d4GXB~2f!alc)z zg~?yypbXg^+_kIn!#Za#$uJx3>#kuPNc07AqrVHvkprEY#YfP)+2O=gz6Q9w4EFV!|0jBQEEd|&?jSMv^H&`2a@1`)G|oxyPew5Np$NTRHPldU< z9S2$ZmlM;O)pH`27szdIUkgU3ENtu?0)r^c-9scoqu;hD%w^IROylAwZg&KE(3#hx z2yN-+&Yn>eNLw&P%8w6KY&op=D)&Zu7gL1I38(kK_w78Q-NeoT|0hCLXYIV;%u_CPn<ZBu37}t| zHP_TW=l(=ztv2F27+P8*Z#-HgR|{30Y!`hpI;#dq%prCIkI@Xr3OxV3Q(S_@V|x$3 zY*h~6`D`W?dOO-G2Yu6Gh;*97%3eHPU0>)m%dv25GP9 z7qNSwAJQ*^jFy6zk;_i4fkGrqljuI9N_!21;#p@mIUu7QtSIc>5e$|v+4%u5*|8mj zww*!XmzOC;2|N!M6WgK@xfnR&r1uZy-i6gEduWTDX_I0$QZl;1%EqerSXJuq%rR4w z_r=p-l+=vL?eFu+&$Iz6yihqcfz!{O_r&V08OtG%3xbm4Op<4q*P(<@Q1%Kp$fO)B zWa&?mk#|%D5jgONU_t}W9~HT{wUBP)TAzImRIO28GcN-9Tjug-71m#sgT_)?+vuRS zP3;>7Z}(fJzJG;uNe%WUT0mrrbPP8JRkW@`n{@||_S5!fCfm)COFEPmPlD}(oNI2h z&qJwW`Cxr@V9{r9cgnk@7mN=L45F>U)~{DOg6P*0YpX&2LJ#7 literal 0 HcmV?d00001