@@ -17,8 +17,15 @@ use processing_input::{
1717} ;
1818use processing_render:: surface:: { MonitorWorkarea , WindowControls } ;
1919
20+ /// A single GLFW instance drives every window (GLFW's event pump is global). The
21+ /// main window is `windows[0]`; `create_window` appends more.
2022pub struct GlfwContext {
2123 glfw : Glfw ,
24+ windows : Vec < ManagedWindow > ,
25+ }
26+
27+ /// Per-window state. One `GlfwContext` owns many of these on the shared instance.
28+ struct ManagedWindow {
2229 window : PWindow ,
2330 events : GlfwReceiver < ( f64 , WindowEvent ) > ,
2431 surface : Option < Entity > ,
@@ -58,15 +65,31 @@ impl Default for AppliedWindow {
5865}
5966
6067impl GlfwContext {
61- pub fn new ( width : u32 , height : u32 ) -> Result < Self > {
68+ pub fn new ( width : u32 , height : u32 , transparent : bool ) -> Result < Self > {
6269 let mut glfw = glfw:: init ( glfw:: fail_on_errors) . unwrap ( ) ;
70+ let main = Self :: spawn_window ( & mut glfw, width, height, transparent, "Processing" ) ;
71+ Ok ( Self {
72+ glfw,
73+ windows : vec ! [ main] ,
74+ } )
75+ }
6376
77+ /// Create a GLFW window on the shared instance and return its per-window state.
78+ fn spawn_window (
79+ glfw : & mut Glfw ,
80+ width : u32 ,
81+ height : u32 ,
82+ transparent : bool ,
83+ title : & str ,
84+ ) -> ManagedWindow {
6485 glfw. window_hint ( glfw:: WindowHint :: ClientApi ( glfw:: ClientApiHint :: NoApi ) ) ;
6586 glfw. window_hint ( glfw:: WindowHint :: Visible ( false ) ) ;
66- glfw. window_hint ( glfw:: WindowHint :: TransparentFramebuffer ( true ) ) ;
87+ // Window transparency is an explicit opt-in; an opaque framebuffer is the
88+ // default (a transparent-by-default window is surprising and platform-flaky).
89+ glfw. window_hint ( glfw:: WindowHint :: TransparentFramebuffer ( transparent) ) ;
6790
6891 let ( mut window, events) = glfw
69- . create_window ( width, height, "Processing" , WindowMode :: Windowed )
92+ . create_window ( width, height, title , WindowMode :: Windowed )
7093 . unwrap ( ) ;
7194
7295 window. set_all_polling ( true ) ;
@@ -101,14 +124,13 @@ impl GlfwContext {
101124
102125 window. show ( ) ;
103126
104- Ok ( Self {
105- glfw,
127+ ManagedWindow {
106128 window,
107129 events,
108130 surface : None ,
109131 last_applied : AppliedWindow :: default ( ) ,
110132 windowed_geometry : None ,
111- } )
133+ }
112134 }
113135
114136 fn sync_monitors ( & mut self ) {
@@ -207,67 +229,112 @@ impl GlfwContext {
207229 } ) ;
208230 }
209231
210- #[ cfg( target_os = "macos" ) ]
211- pub fn create_surface ( & mut self , width : u32 , height : u32 ) -> Result < Entity > {
212- use processing_render:: surface_create_macos;
213- let ( scale_factor, _) = self . window . get_content_scale ( ) ;
214- let entity = surface_create_macos (
215- self . window . get_cocoa_window ( ) as u64 ,
216- width,
217- height,
218- scale_factor,
219- ) ?;
220- self . surface = Some ( entity) ;
221- Ok ( entity)
232+ /// Create the render surface for the main window (index 0).
233+ pub fn create_surface ( & mut self , width : u32 , height : u32 , transparent : bool ) -> Result < Entity > {
234+ self . create_surface_for ( 0 , width, height, transparent)
222235 }
223236
224- #[ cfg( target_os = "windows" ) ]
225- pub fn create_surface ( & mut self , width : u32 , height : u32 ) -> Result < Entity > {
226- use processing_render:: surface_create_windows;
227- let ( scale_factor, _) = self . window . get_content_scale ( ) ;
228- let entity = surface_create_windows (
229- self . window . get_win32_window ( ) as u64 ,
230- width,
231- height,
232- scale_factor,
233- ) ?;
234- self . surface = Some ( entity) ;
235- Ok ( entity)
237+ /// Create an additional window on the shared GLFW instance plus its render
238+ /// surface, and return the new window's surface entity.
239+ pub fn add_window (
240+ & mut self ,
241+ width : u32 ,
242+ height : u32 ,
243+ transparent : bool ,
244+ title : & str ,
245+ ) -> Result < Entity > {
246+ let mw = Self :: spawn_window ( & mut self . glfw , width, height, transparent, title) ;
247+ self . windows . push ( mw) ;
248+ let idx = self . windows . len ( ) - 1 ;
249+ self . create_surface_for ( idx, width, height, transparent)
236250 }
237251
238- #[ cfg( all( target_os = "linux" , feature = "wayland" ) ) ]
239- pub fn create_surface ( & mut self , width : u32 , height : u32 ) -> Result < Entity > {
240- use processing_render:: surface_create_wayland;
241- let ( scale_factor, _) = self . window . get_content_scale ( ) ;
242- let entity = surface_create_wayland (
243- self . window . get_wayland_window ( ) as u64 ,
244- self . glfw . get_wayland_display ( ) as u64 ,
245- width,
246- height,
247- scale_factor,
248- ) ?;
249- self . surface = Some ( entity) ;
250- Ok ( entity)
252+ /// The surface entity of the main window, if created.
253+ pub fn main_surface ( & self ) -> Option < Entity > {
254+ self . windows . first ( ) . and_then ( |w| w. surface )
251255 }
252256
253- #[ cfg( all( target_os = "linux" , feature = "x11" ) ) ]
254- pub fn create_surface ( & mut self , width : u32 , height : u32 ) -> Result < Entity > {
255- use processing_render:: surface_create_x11;
256- let ( scale_factor, _) = self . window . get_content_scale ( ) ;
257- let entity = surface_create_x11 (
258- self . window . get_x11_window ( ) as u64 ,
259- self . glfw . get_x11_display ( ) as u64 ,
260- width,
261- height,
262- scale_factor,
263- ) ?;
264- self . surface = Some ( entity) ;
257+ fn create_surface_for (
258+ & mut self ,
259+ idx : usize ,
260+ width : u32 ,
261+ height : u32 ,
262+ transparent : bool ,
263+ ) -> Result < Entity > {
264+ let ( scale_factor, _) = self . windows [ idx] . window . get_content_scale ( ) ;
265+
266+ #[ cfg( target_os = "macos" ) ]
267+ let entity = {
268+ use processing_render:: surface_create_macos;
269+ let handle = self . windows [ idx] . window . get_cocoa_window ( ) as u64 ;
270+ surface_create_macos ( handle, width, height, scale_factor, transparent) ?
271+ } ;
272+ #[ cfg( target_os = "windows" ) ]
273+ let entity = {
274+ use processing_render:: surface_create_windows;
275+ let handle = self . windows [ idx] . window . get_win32_window ( ) as u64 ;
276+ surface_create_windows ( handle, width, height, scale_factor, transparent) ?
277+ } ;
278+ #[ cfg( all( target_os = "linux" , feature = "wayland" ) ) ]
279+ let entity = {
280+ use processing_render:: surface_create_wayland;
281+ let wh = self . windows [ idx] . window . get_wayland_window ( ) as u64 ;
282+ let dh = self . glfw . get_wayland_display ( ) as u64 ;
283+ surface_create_wayland ( wh, dh, width, height, scale_factor, transparent) ?
284+ } ;
285+ #[ cfg( all( target_os = "linux" , feature = "x11" , not( feature = "wayland" ) ) ) ]
286+ let entity = {
287+ use processing_render:: surface_create_x11;
288+ let wh = self . windows [ idx] . window . get_x11_window ( ) as u64 ;
289+ let dh = self . glfw . get_x11_display ( ) as u64 ;
290+ surface_create_x11 ( wh, dh, width, height, scale_factor, transparent) ?
291+ } ;
292+
293+ self . windows [ idx] . surface = Some ( entity) ;
265294 Ok ( entity)
266295 }
267296
268297 pub fn poll_events ( & mut self ) -> bool {
269298 self . glfw . poll_events ( ) ;
299+ self . sync_monitors ( ) ;
270300
301+ // GLFW's pump is global; flush + sync each window on the shared instance.
302+ // A closed secondary window is dropped; a closed main window ends the loop.
303+ let GlfwContext { glfw, windows } = self ;
304+ let mut main_open = true ;
305+ let mut i = 0 ;
306+ while i < windows. len ( ) {
307+ if windows[ i] . poll ( glfw) {
308+ i += 1 ;
309+ } else if i == 0 {
310+ main_open = false ;
311+ i += 1 ;
312+ } else {
313+ windows[ i] . window . hide ( ) ;
314+ windows. remove ( i) ;
315+ }
316+ }
317+
318+ // Input is accumulated per-window above; commit it once for the frame.
319+ if input_flush ( ) . is_err ( ) {
320+ return false ;
321+ }
322+ main_open
323+ }
324+
325+ /// Content scale (DPI) of the main window.
326+ pub fn content_scale ( & self ) -> f32 {
327+ self . windows
328+ . first ( )
329+ . map ( |w| w. window . get_content_scale ( ) . 0 )
330+ . unwrap_or ( 1.0 )
331+ }
332+ }
333+
334+ impl ManagedWindow {
335+ /// Flush this window's events and sync its OS state; returns whether it's
336+ /// still open. Input is committed once per frame by the caller.
337+ fn poll ( & mut self , glfw : & mut Glfw ) -> bool {
271338 let surface = match self . surface {
272339 Some ( s) => s,
273340 None => {
@@ -349,22 +416,18 @@ impl GlfwContext {
349416 processing_render:: surface_resize ( surface, width as u32 , height as u32 ) . unwrap ( ) ;
350417 }
351418
352- let Ok ( _) = input_flush ( ) else {
353- return false ;
354- } ;
355419 self . sync_cursor ( surface) ;
356- self . sync_monitors ( ) ;
357- self . sync_window ( surface) ;
420+ self . sync_window ( glfw, surface) ;
358421
359422 true
360423 }
361424
362- fn sync_window ( & mut self , surface : Entity ) {
425+ fn sync_window ( & mut self , glfw : & mut Glfw , surface : Entity ) {
363426 let Some ( desired) = read_desired_window ( surface) else {
364427 return ;
365428 } ;
366429
367- self . apply_window ( & desired) ;
430+ self . apply_window ( glfw , & desired) ;
368431
369432 if desired. iconify {
370433 self . window . iconify ( ) ;
@@ -412,7 +475,7 @@ impl GlfwContext {
412475 self . last_applied . position
413476 }
414477
415- fn apply_window ( & mut self , desired : & DesiredWindow ) {
478+ fn apply_window ( & mut self , glfw : & mut Glfw , desired : & DesiredWindow ) {
416479 let last = & mut self . last_applied ;
417480
418481 if desired. title != last. title {
@@ -462,11 +525,11 @@ impl GlfwContext {
462525 last. opacity = opacity;
463526 }
464527 if desired. fullscreen_on != last. fullscreen_on {
465- self . apply_fullscreen ( desired. fullscreen_on ) ;
528+ self . apply_fullscreen ( glfw , desired. fullscreen_on ) ;
466529 }
467530 }
468531
469- fn apply_fullscreen ( & mut self , target : Option < Entity > ) {
532+ fn apply_fullscreen ( & mut self , glfw : & mut Glfw , target : Option < Entity > ) {
470533 match target {
471534 Some ( monitor_entity) => {
472535 if self . last_applied . fullscreen_on . is_none ( ) {
@@ -476,7 +539,7 @@ impl GlfwContext {
476539 }
477540 let target_name = monitor_name ( monitor_entity) ;
478541 let window = & mut self . window ;
479- let applied = self . glfw . with_connected_monitors ( |_, monitors| {
542+ let applied = glfw. with_connected_monitors ( |_, monitors| {
480543 let Some ( monitor) = monitors
481544 . iter ( )
482545 . find ( |m| m. get_name ( ) == target_name)
@@ -506,11 +569,6 @@ impl GlfwContext {
506569 }
507570 }
508571
509- pub fn content_scale ( & self ) -> f32 {
510- let ( s, _) = self . window . get_content_scale ( ) ;
511- s
512- }
513-
514572 fn sync_cursor ( & mut self , surface : Entity ) {
515573 use bevy:: window:: CursorGrabMode ;
516574
0 commit comments