1pub use egui;
2use egui::{pos2, vec2, CursorIcon, Event, Rect, Vec2};
3pub use egui_image::RetainedEguiImage;
4mod backend;
5pub use backend::{CallbackFn, RenderPass, ScreenDescriptor};
6pub use fltk;
7use fltk::{
8 app,
9 enums::{self, Cursor},
10 prelude::{FltkError, ImageExt, WindowExt},
11};
12pub use pollster;
13use std::time::Instant;
14pub use wgpu;
15mod clipboard;
16mod egui_image;
17use clipboard::Clipboard;
18
19pub trait PPU {
21 fn pixels_per_unit(&self) -> f32;
22}
23
24impl PPU for fltk::window::Window {
25 fn pixels_per_unit(&self) -> f32 {
26 self.pixels_per_unit()
27 }
28}
29
30#[cfg(feature = "enable-glwindow")]
31impl PPU for fltk::window::GlWindow {
32 fn pixels_per_unit(&self) -> f32 {
33 self.pixels_per_unit()
34 }
35}
36
37pub fn begin_with<'a, W>(
39 window: &mut W,
40 render_pass: RenderPass<'a>,
41 surface: wgpu::Surface,
42 surface_config: wgpu::SurfaceConfiguration,
43) -> (Painter<'a>, EguiState)
44where
45 W: WindowExt + PPU,
46{
47 app::set_screen_scale(window.screen_num(), 1.0);
48 app::keyboard_screen_scaling(false);
49 let ppu = window.pixels_per_unit();
50 let x = window.width();
51 let y = window.height();
52 let rect = egui::vec2(x as _, y as _) / ppu;
53 let screen_rect = egui::Rect::from_min_size(egui::Pos2::new(0f32, 0f32), rect);
54
55 let painter = Painter {
56 render_pass,
57 surface,
58 surface_config,
59 encoder: wgpu::CommandEncoderDescriptor {
60 label: Some("encoder"),
61 },
62 };
63
64 let state = EguiState {
65 _window_resized: false,
66 fuse_cursor: FusedCursor::new(),
67 pointer_pos: egui::Pos2::new(0.0, 0.0),
68 input: egui::RawInput {
69 screen_rect: Some(screen_rect),
70 pixels_per_point: Some(ppu),
71 ..Default::default()
72 },
73 clipboard: clipboard::Clipboard::default(),
74 _mouse_btn_pressed: false,
75 scroll_factor: 12.0,
76 zoom_factor: 8.0,
77 screen_descriptor: ScreenDescriptor {
78 size_in_pixels: [x as _, y as _],
79 pixels_per_point: ppu,
80 },
81 };
82 (painter, state)
83}
84
85pub struct Painter<'a> {
86 pub render_pass: RenderPass<'a>,
87 pub surface: wgpu::Surface,
88 pub surface_config: wgpu::SurfaceConfiguration,
89 encoder: wgpu::CommandEncoderDescriptor<'a>,
90}
91
92impl<'a> Painter<'a> {
93 pub fn paint_with_rpass<'rpass>(
95 &'rpass mut self,
96 rpass: &mut wgpu::RenderPass<'rpass>,
97 device: &wgpu::Device,
98 queue: &wgpu::Queue,
99 screen_descriptor: &ScreenDescriptor,
100 clipped_primitive: Vec<egui::ClippedPrimitive>,
101 texture: egui::TexturesDelta,
102 ) {
103 if texture.free.len() > 0 {
104 texture.free.into_iter().for_each(|id| {
105 self.render_pass.free_texture(&id);
106 });
107 }
108
109 for (id, img_del) in texture.set {
110 self.render_pass.update_texture(device, queue, id, &img_del);
111 }
112
113 self.render_pass
114 .update_buffers(device, queue, &clipped_primitive, screen_descriptor);
115
116 self.render_pass
117 .execute_with_renderpass(rpass, clipped_primitive, screen_descriptor);
118 }
119
120 pub fn paint_jobs(
121 &mut self,
122 device: &wgpu::Device,
123 queue: &wgpu::Queue,
124 screen_descriptor: &ScreenDescriptor,
125 clipped_primitive: Vec<egui::ClippedPrimitive>,
126 texture: egui::TexturesDelta,
127 ) {
128 {
129 let size = screen_descriptor.size_in_pixels;
130 self.surface_config.width = size[0];
131 self.surface_config.height = size[1];
132 self.surface.configure(device, &self.surface_config);
133 }
134
135 let output_frame = match self.surface.get_current_texture() {
137 Ok(frame) => {
138 let mut encoder = device.create_command_encoder(&self.encoder);
139
140 if texture.free.len() > 0 {
141 texture.free.into_iter().for_each(|id| {
142 self.render_pass.free_texture(&id);
143 });
144 }
145
146 for (id, img_del) in texture.set {
147 self.render_pass.update_texture(device, queue, id, &img_del);
148 }
149
150 self.render_pass.update_buffers(
151 device,
152 queue,
153 &clipped_primitive,
154 screen_descriptor,
155 );
156
157 self.render_pass.execute(
158 &mut encoder,
159 &frame.texture.create_view(&self.render_pass.tex_view_desc),
160 clipped_primitive,
161 screen_descriptor,
162 Some(wgpu::Color::BLACK),
163 );
164
165 let cm_buffer = encoder.finish();
167 queue.submit(Some(cm_buffer));
168 frame
169 }
170 Err(e) => return eprintln!("Dropped frame with error: {}", e),
171 };
172
173 output_frame.present();
175 }
176}
177
178pub fn get_frame_time(start_time: Instant) -> f32 {
180 (Instant::now() - start_time).as_secs_f64() as _
181}
182
183pub struct FusedCursor {
185 pub cursor_icon: Cursor,
186}
187
188const ARROW: enums::Cursor = enums::Cursor::Arrow;
189
190impl FusedCursor {
191 pub fn new() -> Self {
193 Self { cursor_icon: ARROW }
194 }
195}
196
197impl Default for FusedCursor {
198 fn default() -> Self {
199 Self::new()
200 }
201}
202
203pub struct EguiState {
205 _window_resized: bool,
206 pub fuse_cursor: FusedCursor,
207 pub pointer_pos: egui::Pos2,
208 input: egui::RawInput,
209 pub clipboard: Clipboard,
210 pub scroll_factor: f32,
212 pub zoom_factor: f32,
214 _mouse_btn_pressed: bool,
215 pub screen_descriptor: ScreenDescriptor,
216}
217
218impl EguiState {
219 pub fn fuse_input<W>(&mut self, win: &mut W, event: enums::Event)
221 where
222 W: WindowExt + PPU,
223 {
224 input_to_egui(win, event, self);
225 }
226
227 pub fn window_resized(&mut self) -> bool {
228 let tmp = self._window_resized;
229 self._window_resized = false;
230 tmp
231 }
232
233 pub fn mouse_btn_pressed(&self) -> bool {
234 self._mouse_btn_pressed
235 }
236
237 pub fn fuse_output<W>(&mut self, win: &mut W, egui_output: egui::PlatformOutput)
239 where
240 W: WindowExt,
241 {
242 if win.damage() {
243 win.clear_damage();
244 }
245
246 let copied_text = &egui_output.copied_text;
247 if !copied_text.is_empty() {
248 self.clipboard.set(copied_text.into());
249 }
250 translate_cursor(win, &mut self.fuse_cursor, egui_output.cursor_icon);
251 }
252
253 pub fn set_visual_scale(&mut self, size: f32) {
255 self.input.pixels_per_point = Some(size);
257 self.screen_descriptor.pixels_per_point = size;
258
259 let size_in_pixels = self.screen_descriptor.size_in_pixels;
260
261 let rect = vec2(size_in_pixels[0] as _, size_in_pixels[1] as _) / size;
263 self.input.screen_rect = Some(Rect::from_min_size(Default::default(), rect));
264 }
265
266 pub fn pixels_per_point(&self) -> f32 {
267 self.screen_descriptor.pixels_per_point
268 }
269
270 pub fn take_input(&mut self) -> egui::RawInput {
271 let pixels_per_point = self.input.pixels_per_point;
272 let take = self.input.take();
273 self.input.pixels_per_point = Some(self.screen_descriptor.pixels_per_point);
274 if let Some(ppp) = pixels_per_point {
275 self.screen_descriptor.pixels_per_point = ppp;
276 }
277 take
278 }
279
280 pub fn start_time(&mut self, elapsed: f64) {
282 self.input.time = Some(elapsed);
283 }
284}
285
286pub fn input_to_egui<W>(win: &mut W, event: enums::Event, state: &mut EguiState)
288where
289 W: WindowExt + PPU,
290{
291 match event {
292 enums::Event::Resize => {
293 state.screen_descriptor.size_in_pixels = [win.width() as _, win.height() as _];
294 state.set_visual_scale(state.pixels_per_point());
295 state._window_resized = true;
296 }
297 enums::Event::Push => {
299 let mouse_btn = match app::event_mouse_button() {
300 app::MouseButton::Left => Some(egui::PointerButton::Primary),
301 app::MouseButton::Middle => Some(egui::PointerButton::Middle),
302 app::MouseButton::Right => Some(egui::PointerButton::Secondary),
303 _ => None,
304 };
305 if let Some(pressed) = mouse_btn {
306 state._mouse_btn_pressed = true;
307 state.input.events.push(egui::Event::PointerButton {
308 pos: state.pointer_pos,
309 button: pressed,
310 pressed: true,
311 modifiers: state.input.modifiers,
312 });
313 }
314 }
315
316 enums::Event::Released => {
318 let mouse_btn = match app::event_mouse_button() {
320 app::MouseButton::Left => Some(egui::PointerButton::Primary),
321 app::MouseButton::Middle => Some(egui::PointerButton::Middle),
322 app::MouseButton::Right => Some(egui::PointerButton::Secondary),
323 _ => None,
324 };
325 if let Some(released) = mouse_btn {
326 state._mouse_btn_pressed = false;
327 state.input.events.push(egui::Event::PointerButton {
328 pos: state.pointer_pos,
329 button: released,
330 pressed: false,
331 modifiers: state.input.modifiers,
332 });
333 }
334 }
335
336 enums::Event::Move | enums::Event::Drag => {
337 let (x, y) = app::event_coords();
338 let ppp = state.pixels_per_point();
339 state.pointer_pos = pos2(x as f32 / ppp, y as f32 / ppp);
340 state
341 .input
342 .events
343 .push(egui::Event::PointerMoved(state.pointer_pos))
344 }
345
346 enums::Event::KeyUp => {
347 if let Some(key) = translate_virtual_key_code(app::event_key()) {
348 let keymod = app::event_state();
349 state.input.modifiers = egui::Modifiers {
350 alt: (keymod & enums::EventState::Alt == enums::EventState::Alt),
351 ctrl: (keymod & enums::EventState::Ctrl == enums::EventState::Ctrl),
352 shift: (keymod & enums::EventState::Shift == enums::EventState::Shift),
353 mac_cmd: keymod & enums::EventState::Meta == enums::EventState::Meta,
354
355 command: (keymod & enums::EventState::Command == enums::EventState::Command),
357 };
358 state.input.events.push(egui::Event::Key {
359 key,
360 pressed: false,
361 modifiers: state.input.modifiers,
362 });
363 }
364 }
365
366 enums::Event::KeyDown => {
367 if let Some(c) = app::event_text().chars().next() {
368 if let Some(del) = app::compose() {
369 state.input.events.push(egui::Event::Text(c.to_string()));
370 if del != 0 {
371 app::compose_reset();
372 }
373 }
374 }
375 if let Some(key) = translate_virtual_key_code(app::event_key()) {
376 let keymod = app::event_state();
377 state.input.modifiers = egui::Modifiers {
378 alt: (keymod & enums::EventState::Alt == enums::EventState::Alt),
379 ctrl: (keymod & enums::EventState::Ctrl == enums::EventState::Ctrl),
380 shift: (keymod & enums::EventState::Shift == enums::EventState::Shift),
381 mac_cmd: keymod & enums::EventState::Meta == enums::EventState::Meta,
382
383 command: (keymod & enums::EventState::Command == enums::EventState::Command),
385 };
386 state.input.events.push(egui::Event::Key {
387 key,
388 pressed: true,
389 modifiers: state.input.modifiers,
390 });
391 if state.input.modifiers.command && key == egui::Key::C {
392 state.input.events.push(egui::Event::Copy);
394 } else if state.input.modifiers.command && key == egui::Key::X {
395 state.input.events.push(egui::Event::Cut);
397 } else if state.input.modifiers.command && key == egui::Key::V {
398 if let Some(value) = state.clipboard.get() {
399 state.input.events.push(egui::Event::Text(value));
400 }
401 }
402 }
403 }
404
405 enums::Event::MouseWheel => {
406 if app::is_event_ctrl() {
407 let zoom_factor = state.zoom_factor;
408 match app::event_dy() {
409 app::MouseWheel::Up => {
410 let delta = vec2(1., -1.) * zoom_factor;
411
412 state
414 .input
415 .events
416 .push(Event::Zoom((delta.y / 200.0).exp()));
417 }
418 app::MouseWheel::Down => {
419 let delta = vec2(-1., 1.) * zoom_factor;
420
421 state
423 .input
424 .events
425 .push(Event::Zoom((delta.y / 200.0).exp()));
426 }
427 _ => (),
428 }
429 } else {
430 let scroll_factor = state.scroll_factor;
431 match app::event_dy() {
432 app::MouseWheel::Up => {
433 state.input.events.push(Event::Scroll(Vec2 {
434 x: 0.,
435 y: -scroll_factor,
436 }));
437 }
438 app::MouseWheel::Down => {
439 state.input.events.push(Event::Scroll(Vec2 {
440 x: 0.,
441 y: scroll_factor,
442 }));
443 }
444 _ => (),
445 }
446 }
447 }
448
449 _ => {
450 }
452 }
453}
454
455pub fn translate_virtual_key_code(key: enums::Key) -> Option<egui::Key> {
457 match key {
458 enums::Key::Left => Some(egui::Key::ArrowLeft),
459 enums::Key::Up => Some(egui::Key::ArrowUp),
460 enums::Key::Right => Some(egui::Key::ArrowRight),
461 enums::Key::Down => Some(egui::Key::ArrowDown),
462 enums::Key::Escape => Some(egui::Key::Escape),
463 enums::Key::Tab => Some(egui::Key::Tab),
464 enums::Key::BackSpace => Some(egui::Key::Backspace),
465 enums::Key::Insert => Some(egui::Key::Insert),
466 enums::Key::Home => Some(egui::Key::Home),
467 enums::Key::Delete => Some(egui::Key::Delete),
468 enums::Key::End => Some(egui::Key::End),
469 enums::Key::PageDown => Some(egui::Key::PageDown),
470 enums::Key::PageUp => Some(egui::Key::PageUp),
471 enums::Key::Enter => Some(egui::Key::Enter),
472 _ => {
473 if let Some(k) = key.to_char() {
474 match k {
475 ' ' => Some(egui::Key::Space),
476 'a' => Some(egui::Key::A),
477 'b' => Some(egui::Key::B),
478 'c' => Some(egui::Key::C),
479 'd' => Some(egui::Key::D),
480 'e' => Some(egui::Key::E),
481 'f' => Some(egui::Key::F),
482 'g' => Some(egui::Key::G),
483 'h' => Some(egui::Key::H),
484 'i' => Some(egui::Key::I),
485 'j' => Some(egui::Key::J),
486 'k' => Some(egui::Key::K),
487 'l' => Some(egui::Key::L),
488 'm' => Some(egui::Key::M),
489 'n' => Some(egui::Key::N),
490 'o' => Some(egui::Key::O),
491 'p' => Some(egui::Key::P),
492 'q' => Some(egui::Key::Q),
493 'r' => Some(egui::Key::R),
494 's' => Some(egui::Key::S),
495 't' => Some(egui::Key::T),
496 'u' => Some(egui::Key::U),
497 'v' => Some(egui::Key::V),
498 'w' => Some(egui::Key::W),
499 'x' => Some(egui::Key::X),
500 'y' => Some(egui::Key::Y),
501 'z' => Some(egui::Key::Z),
502 '0' => Some(egui::Key::Num0),
503 '1' => Some(egui::Key::Num1),
504 '2' => Some(egui::Key::Num2),
505 '3' => Some(egui::Key::Num3),
506 '4' => Some(egui::Key::Num4),
507 '5' => Some(egui::Key::Num5),
508 '6' => Some(egui::Key::Num6),
509 '7' => Some(egui::Key::Num7),
510 '8' => Some(egui::Key::Num8),
511 '9' => Some(egui::Key::Num9),
512 _ => None,
513 }
514 } else {
515 None
516 }
517 }
518 }
519}
520
521pub fn translate_cursor<W>(win: &mut W, fused: &mut FusedCursor, cursor_icon: CursorIcon)
523where
524 W: WindowExt,
525{
526 let tmp_icon = match cursor_icon {
527 CursorIcon::None => enums::Cursor::None,
528 CursorIcon::Default => enums::Cursor::Arrow,
529 CursorIcon::Help => enums::Cursor::Help,
530 CursorIcon::PointingHand => enums::Cursor::Hand,
531 CursorIcon::ResizeHorizontal => enums::Cursor::WE,
532 CursorIcon::ResizeNeSw => enums::Cursor::NESW,
533 CursorIcon::ResizeNwSe => enums::Cursor::NWSE,
534 CursorIcon::ResizeVertical => enums::Cursor::NS,
535 CursorIcon::Text => enums::Cursor::Insert,
536 CursorIcon::Crosshair => enums::Cursor::Cross,
537 CursorIcon::NotAllowed | CursorIcon::NoDrop => enums::Cursor::Wait,
538 CursorIcon::Wait => enums::Cursor::Wait,
539 CursorIcon::Progress => enums::Cursor::Wait,
540 CursorIcon::Grab => enums::Cursor::Hand,
541 CursorIcon::Grabbing => enums::Cursor::Move,
542 CursorIcon::Move => enums::Cursor::Move,
543
544 _ => enums::Cursor::Arrow,
545 };
546
547 if tmp_icon != fused.cursor_icon {
548 fused.cursor_icon = tmp_icon;
549 win.set_cursor(tmp_icon);
550 }
551}
552
553pub struct Compat {
555 setup: bool,
556}
557
558impl Default for Compat {
559 fn default() -> Self {
560 Self { setup: true }
561 }
562}
563
564impl Compat {
565 pub fn needs_setup(&mut self) -> bool {
567 if self.setup {
568 self.setup = false;
569 return true;
570 }
571 self.setup
572 }
573}
574
575pub struct Timer {
576 timer: u32,
577 elapse: u32,
578 duration: f32,
579}
580
581impl Timer {
582 pub fn new(elapse: u32) -> Self {
584 let _elapse = elapse * 180;
585 let duration = _elapse as f32 / 1000.0;
586 Self {
587 timer: 0,
588 elapse: elapse * 6,
589 duration,
590 }
591 }
592
593 pub fn elapsed(&mut self) -> bool {
595 if self.timer >= self.elapse {
596 self.timer = 0;
597 return true;
598 }
599 self.timer += 1;
600 app::sleep(self.duration.into());
601 false
602 }
603}
604
605pub trait EguiImageConvertible<I>
606where
607 I: ImageExt,
608{
609 fn egui_image(self, debug_name: &str) -> Result<RetainedEguiImage, FltkError>;
610}
611
612impl<I> EguiImageConvertible<I> for I
613where
614 I: ImageExt,
615{
616 fn egui_image(self, debug_name: &str) -> Result<RetainedEguiImage, FltkError> {
618 let size = [self.data_w() as _, self.data_h() as _];
619 let color_image = egui::ColorImage::from_rgba_unmultiplied(
620 size,
621 &self
622 .to_rgb()?
623 .convert(enums::ColorDepth::Rgba8)?
624 .to_rgb_data(),
625 );
626
627 Ok(RetainedEguiImage::from_color_image(debug_name, color_image))
628 }
629}
630
631pub trait EguiSvgConvertible {
632 fn egui_svg_image(self, debug_name: &str) -> Result<RetainedEguiImage, FltkError>;
633}
634
635impl EguiSvgConvertible for fltk::image::SvgImage {
636 fn egui_svg_image(mut self, debug_name: &str) -> Result<RetainedEguiImage, FltkError> {
638 self.normalize();
639 let size = [self.data_w() as usize, self.data_h() as usize];
640 let color_image = egui::ColorImage::from_rgba_unmultiplied(
641 size,
642 &self
643 .to_rgb()?
644 .convert(enums::ColorDepth::Rgba8)?
645 .to_rgb_data(),
646 );
647
648 Ok(RetainedEguiImage::from_color_image(debug_name, color_image))
649 }
650}
651pub trait ColorImageExt {
653 fn from_vec_color32(size: [usize; 2], vec: Vec<egui::Color32>) -> Self;
654
655 fn from_color32_slice(size: [usize; 2], slice: &[egui::Color32]) -> Self;
656}
657
658impl ColorImageExt for egui::ColorImage {
659 fn from_vec_color32(size: [usize; 2], vec: Vec<egui::Color32>) -> Self {
660 let mut pixels: Vec<u8> = Vec::with_capacity(vec.len() * 4);
661 vec.into_iter().for_each(|x| {
662 pixels.push(x[0]);
663 pixels.push(x[1]);
664 pixels.push(x[2]);
665 pixels.push(x[3]);
666 });
667 egui::ColorImage::from_rgba_unmultiplied(size, &pixels)
668 }
669
670 fn from_color32_slice(size: [usize; 2], slice: &[egui::Color32]) -> Self {
671 let mut pixels: Vec<u8> = Vec::with_capacity(slice.len() * 4);
672 slice.into_iter().for_each(|x| {
673 pixels.push(x[0]);
674 pixels.push(x[1]);
675 pixels.push(x[2]);
676 pixels.push(x[3]);
677 });
678 egui::ColorImage::from_rgba_unmultiplied(size, &pixels)
679 }
680}
681
682pub trait TextureHandleExt {
684 fn from_vec_u8(
686 ctx: &egui::Context,
687 debug_name: &str,
688 size: [usize; 2],
689 vec: Vec<u8>,
690 ) -> egui::TextureHandle;
691
692 fn from_u8_slice(
693 ctx: &egui::Context,
694 debug_name: &str,
695 size: [usize; 2],
696 slice: &[u8],
697 ) -> egui::TextureHandle;
698
699 fn from_vec_color32(
700 ctx: &egui::Context,
701 debug_name: &str,
702 size: [usize; 2],
703 vec: Vec<egui::Color32>,
704 ) -> egui::TextureHandle;
705
706 fn from_color32_slice(
707 ctx: &egui::Context,
708 debug_name: &str,
709 size: [usize; 2],
710 slice: &[egui::Color32],
711 ) -> egui::TextureHandle;
712}
713
714impl TextureHandleExt for egui::TextureHandle {
715 fn from_vec_u8(ctx: &egui::Context, debug_name: &str, size: [usize; 2], vec: Vec<u8>) -> Self {
716 let color_image = egui::ColorImage::from_rgba_unmultiplied(size, &vec);
717 drop(vec);
718 ctx.load_texture(debug_name, color_image, egui::TextureFilter::Linear)
719 }
720
721 fn from_u8_slice(
722 ctx: &egui::Context,
723 debug_name: &str,
724 size: [usize; 2],
725 slice: &[u8],
726 ) -> Self {
727 let color_image = egui::ColorImage::from_rgba_unmultiplied(size, slice);
728 ctx.load_texture(debug_name, color_image, egui::TextureFilter::Linear)
729 }
730
731 fn from_vec_color32(
732 ctx: &egui::Context,
733 debug_name: &str,
734 size: [usize; 2],
735 vec: Vec<egui::Color32>,
736 ) -> Self {
737 let mut pixels: Vec<u8> = Vec::with_capacity(vec.len() * 4);
738 vec.into_iter().for_each(|x| {
739 pixels.push(x[0]);
740 pixels.push(x[1]);
741 pixels.push(x[2]);
742 pixels.push(x[3]);
743 });
744 let color_image = egui::ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
745 ctx.load_texture(debug_name, color_image, egui::TextureFilter::Linear)
746 }
747
748 fn from_color32_slice(
749 ctx: &egui::Context,
750 debug_name: &str,
751 size: [usize; 2],
752 slice: &[egui::Color32],
753 ) -> Self {
754 let mut pixels: Vec<u8> = Vec::with_capacity(slice.len() * 4);
755 slice.into_iter().for_each(|x| {
756 pixels.push(x[0]);
757 pixels.push(x[1]);
758 pixels.push(x[2]);
759 pixels.push(x[3]);
760 });
761 let color_image = egui::ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
762 ctx.load_texture(debug_name, color_image, egui::TextureFilter::Linear)
763 }
764}
765
766pub trait RWHandleExt {
769 fn use_compat(&self) -> RwhCompat;
771}
772
773impl RWHandleExt for fltk::window::Window {
774 fn use_compat(&self) -> RwhCompat {
775 RwhCompat(self.raw_handle())
776 }
777}
778
779#[cfg(feature = "enable-glwindow")]
780impl RWHandleExt for fltk::window::GlWindow {
781 fn use_compat(&self) -> RwhCompat {
782 RwhCompat(self.raw_handle())
783 }
784}
785
786pub struct RwhCompat(fltk::window::RawHandle);
789
790unsafe impl raw_window_handle::HasRawWindowHandle for RwhCompat {
791 fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
792 #[cfg(target_os = "windows")]
793 {
794 let mut handle = raw_window_handle::Win32Handle::empty();
795 handle.hwnd = self.0;
796 handle.hinstance = fltk::app::display();
797 return raw_window_handle::RawWindowHandle::Win32(handle);
798 }
799
800 #[cfg(target_os = "macos")]
801 {
802 use std::os::raw::c_void;
803
804 let raw = self.0;
805 extern "C" {
806 pub fn cfltk_getContentView(xid: *mut c_void) -> *mut c_void;
807 }
808 let cv = unsafe { cfltk_getContentView(raw) };
809 let mut handle = raw_window_handle::AppKitHandle::empty();
810 handle.ns_window = raw;
811 handle.ns_view = cv as _;
812 return raw_window_handle::RawWindowHandle::AppKit(handle);
813 }
814
815 #[cfg(target_os = "android")]
816 {
817 let mut handle = raw_window_handle::AndroidNdkHandle::empty();
818 handle.a_native_window = self.0;
819 return raw_window_handle::RawWindowHandle::AndroidNdk(handle);
820 }
821
822 #[cfg(any(
823 target_os = "linux",
824 target_os = "dragonfly",
825 target_os = "freebsd",
826 target_os = "netbsd",
827 target_os = "openbsd",
828 ))]
829 {
830 #[cfg(not(feature = "wayland"))]
831 {
832 let mut handle = raw_window_handle::XlibHandle::empty();
833 handle.window = self.0;
834 handle.display = fltk::app::display();
835 return raw_window_handle::RawWindowHandle::Xlib(handle);
836 }
837
838 #[cfg(feature = "wayland")]
839 {
840 let mut handle = raw_window_handle::WaylandHandle::empty();
841 handle.surface = self.0;
842 handle.display = fltk::app::display();
843 return raw_window_handle::RawWindowHandle::Wayland(handle);
844 }
845 }
846 }
847}