From db214d1145e46d527a46d1fc2519548d2c4d23f1 Mon Sep 17 00:00:00 2001 From: maxim nikonov Date: Thu, 5 Feb 2026 15:21:34 +0500 Subject: wip: fork --- src/CMakeLists.txt | 1071 ----- src/Fl.cxx | 67 +- src/Fl_Cairo.cxx | 138 +- src/Fl_MacOS_Sys_Menu_Bar.mm | 785 ---- src/Fl_Native_File_Chooser_MAC.mm | 825 ---- src/Fl_Native_File_Chooser_WIN32.cxx | 1061 ----- src/Fl_cocoa.mm | 4793 -------------------- src/Fl_get_key_mac.cxx | 102 - src/Fl_get_key_win32.cxx | 127 - src/Fl_win32.cxx | 2976 ------------ src/Fl_x.cxx | 2 +- src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H | 2 +- src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx | 2 +- src/drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx | 2 +- src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.H | 60 - src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.mm | 527 --- src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm | 439 -- src/drivers/Cocoa/Fl_Cocoa_Printer_Driver.mm | 455 -- src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H | 114 - src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx | 434 -- src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H | 168 - src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx | 294 -- src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H | 51 - src/drivers/Darwin/Fl_Darwin_System_Driver.H | 85 - src/drivers/Darwin/Fl_Darwin_System_Driver.cxx | 339 -- src/drivers/Darwin/fl_macOS_platform_init.cxx | 59 - src/drivers/GDI/Fl_Font.H | 43 - src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.H | 35 - src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx | 95 - src/drivers/GDI/Fl_GDI_Graphics_Driver.H | 230 - src/drivers/GDI/Fl_GDI_Graphics_Driver.cxx | 322 -- src/drivers/GDI/Fl_GDI_Graphics_Driver_arci.cxx | 89 - src/drivers/GDI/Fl_GDI_Graphics_Driver_color.cxx | 259 -- src/drivers/GDI/Fl_GDI_Graphics_Driver_font.cxx | 678 --- src/drivers/GDI/Fl_GDI_Graphics_Driver_image.cxx | 826 ---- .../GDI/Fl_GDI_Graphics_Driver_line_style.cxx | 111 - src/drivers/GDI/Fl_GDI_Graphics_Driver_rect.cxx | 320 -- src/drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx | 231 - src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H | 44 - src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx | 169 - src/drivers/PostScript/Fl_PostScript.cxx | 8 +- src/drivers/PostScript/Fl_PostScript_image.cxx | 2 +- src/drivers/Quartz/Fl_Font.H | 42 - src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H | 39 - .../Quartz/Fl_Quartz_Copy_Surface_Driver.cxx | 71 - src/drivers/Quartz/Fl_Quartz_Graphics_Driver.H | 149 - src/drivers/Quartz/Fl_Quartz_Graphics_Driver.cxx | 133 - .../Quartz/Fl_Quartz_Graphics_Driver_arci.cxx | 65 - .../Quartz/Fl_Quartz_Graphics_Driver_color.cxx | 70 - .../Quartz/Fl_Quartz_Graphics_Driver_font.cxx | 561 --- .../Quartz/Fl_Quartz_Graphics_Driver_image.cxx | 284 -- .../Fl_Quartz_Graphics_Driver_line_style.cxx | 83 - .../Quartz/Fl_Quartz_Graphics_Driver_rect.cxx | 310 -- .../Quartz/Fl_Quartz_Graphics_Driver_vertex.cxx | 98 - .../Quartz/Fl_Quartz_Image_Surface_Driver.H | 38 - .../Quartz/Fl_Quartz_Image_Surface_Driver.cxx | 167 - .../Wayland/Fl_Wayland_Copy_Surface_Driver.H | 34 - .../Wayland/Fl_Wayland_Copy_Surface_Driver.cxx | 60 - src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H | 62 - .../Wayland/Fl_Wayland_Gl_Window_Driver.cxx | 486 -- src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H | 72 - src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx | 310 -- .../Wayland/Fl_Wayland_Image_Surface_Driver.H | 40 - .../Wayland/Fl_Wayland_Image_Surface_Driver.cxx | 185 - src/drivers/Wayland/Fl_Wayland_Screen_Driver.H | 193 - src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx | 2204 --------- src/drivers/Wayland/Fl_Wayland_Window_Driver.H | 185 - src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx | 2191 --------- src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx | 741 --- src/drivers/Wayland/fl_wayland_platform_init.cxx | 157 - src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.H | 62 - src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx | 472 -- src/drivers/WinAPI/Fl_WinAPI_Pen_Driver.cxx | 518 --- src/drivers/WinAPI/Fl_WinAPI_Printer_Driver.cxx | 518 --- src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H | 104 - src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx | 493 -- src/drivers/WinAPI/Fl_WinAPI_System_Driver.H | 124 - src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx | 1134 ----- src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H | 131 - src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx | 731 --- src/drivers/WinAPI/fl_WinAPI_platform_init.cxx | 85 - src/drivers/X11/Fl_X11_Gl_Window_Driver.cxx | 2 - src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H | 2 +- src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx | 2 +- src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H | 2 +- src/fl_call_main.c | 158 - src/fl_dnd_win32.cxx | 559 --- src/fl_encoding_latin1.cxx | 13 + src/fl_encoding_mac_roman.cxx | 117 - src/flstring.h | 5 - src/forms_fselect.cxx | 7 +- src/scandir_win32.c | 164 - 92 files changed, 39 insertions(+), 32834 deletions(-) delete mode 100644 src/CMakeLists.txt delete mode 100644 src/Fl_MacOS_Sys_Menu_Bar.mm delete mode 100644 src/Fl_Native_File_Chooser_MAC.mm delete mode 100644 src/Fl_Native_File_Chooser_WIN32.cxx delete mode 100644 src/Fl_cocoa.mm delete mode 100644 src/Fl_get_key_mac.cxx delete mode 100644 src/Fl_get_key_win32.cxx delete mode 100644 src/Fl_win32.cxx delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.H delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.mm delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Printer_Driver.mm delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H delete mode 100644 src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx delete mode 100644 src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H delete mode 100644 src/drivers/Darwin/Fl_Darwin_System_Driver.H delete mode 100644 src/drivers/Darwin/Fl_Darwin_System_Driver.cxx delete mode 100644 src/drivers/Darwin/fl_macOS_platform_init.cxx delete mode 100644 src/drivers/GDI/Fl_Font.H delete mode 100644 src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.H delete mode 100644 src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver.H delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_arci.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_color.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_font.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_image.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_line_style.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_rect.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx delete mode 100644 src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H delete mode 100644 src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx delete mode 100644 src/drivers/Quartz/Fl_Font.H delete mode 100644 src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H delete mode 100644 src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver.H delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_arci.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_color.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_font.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_image.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_line_style.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_rect.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Graphics_Driver_vertex.cxx delete mode 100644 src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H delete mode 100644 src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx delete mode 100644 src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H delete mode 100644 src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx delete mode 100644 src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H delete mode 100644 src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx delete mode 100644 src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H delete mode 100644 src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx delete mode 100644 src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H delete mode 100644 src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx delete mode 100644 src/drivers/Wayland/Fl_Wayland_Screen_Driver.H delete mode 100644 src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx delete mode 100644 src/drivers/Wayland/Fl_Wayland_Window_Driver.H delete mode 100644 src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx delete mode 100644 src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx delete mode 100644 src/drivers/Wayland/fl_wayland_platform_init.cxx delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.H delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Pen_Driver.cxx delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Printer_Driver.cxx delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_System_Driver.H delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H delete mode 100644 src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx delete mode 100644 src/drivers/WinAPI/fl_WinAPI_platform_init.cxx delete mode 100644 src/fl_call_main.c delete mode 100644 src/fl_dnd_win32.cxx delete mode 100644 src/fl_encoding_mac_roman.cxx delete mode 100644 src/scandir_win32.c (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index cf4196185..000000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,1071 +0,0 @@ -# -# CMakeLists.txt to build the FLTK library using CMake (www.cmake.org) -# -# Copyright 1998-2026 by Bill Spitzak and others. -# -# This library is free software. Distribution and use rights are outlined in -# the file "COPYING" which should have been included with this file. If this -# file is missing or damaged, see the license at: -# -# https://www.fltk.org/COPYING.php -# -# Please see the following page on how to report bugs and issues: -# -# https://www.fltk.org/bugs.php -# - -# Local macro to check existence of a required file with warning message -# -# In: FILENAME (string): File to search -# Out: WARN (string): *Name* of a variable that is set to true if NOT found -# -# Set variable ${WARN} to FALSE before calling this macro - -macro(fl_check_required_file WARN FILENAME) - if(NOT EXISTS ${FILENAME}) - message("*** Required file not found: ${FILENAME} ***") - set(${WARN} TRUE) - endif() -endmacro(fl_check_required_file WARN FILENAME) - -set(CPPFILES - Fl.cxx - Fl_Adjuster.cxx - Fl_Bitmap.cxx - Fl_Browser.cxx - Fl_Browser_.cxx - Fl_Browser_load.cxx - Fl_Box.cxx - Fl_Button.cxx - Fl_Chart.cxx - Fl_Check_Browser.cxx - Fl_Check_Button.cxx - Fl_Choice.cxx - Fl_Clock.cxx - Fl_Color_Chooser.cxx - Fl_Copy_Surface.cxx - Fl_Counter.cxx - Fl_Device.cxx - Fl_Dial.cxx - Fl_Double_Window.cxx - Fl_File_Browser.cxx - Fl_File_Chooser.cxx - Fl_File_Chooser2.cxx - Fl_File_Icon.cxx - Fl_File_Input.cxx - Fl_Flex.cxx - Fl_Graphics_Driver.cxx - Fl_Grid.cxx - Fl_Group.cxx - Fl_Help_View.cxx - Fl_Image.cxx - Fl_Image_Surface.cxx - Fl_Input.cxx - Fl_Input_.cxx - Fl_Input_Choice.cxx - Fl_Light_Button.cxx - Fl_Menu.cxx - Fl_Menu_.cxx - Fl_Menu_Bar.cxx - Fl_Menu_Button.cxx - Fl_Menu_Window.cxx - Fl_Menu_add.cxx - Fl_Menu_global.cxx - Fl_Message.cxx - Fl_Multi_Label.cxx - Fl_Native_File_Chooser.cxx - Fl_Overlay_Window.cxx - Fl_Pack.cxx - Fl_Paged_Device.cxx - Fl_Pixmap.cxx - Fl_Positioner.cxx - Fl_Preferences.cxx - Fl_Printer.cxx - Fl_Progress.cxx - Fl_Repeat_Button.cxx - Fl_Return_Button.cxx - Fl_Roller.cxx - Fl_Round_Button.cxx - Fl_Scheme.cxx - Fl_Scheme_Choice.cxx - Fl_Screen_Driver.cxx - Fl_Scroll.cxx - Fl_Scrollbar.cxx - Fl_Shared_Image.cxx - Fl_Shortcut_Button.cxx - Fl_Single_Window.cxx - Fl_Slider.cxx - Fl_Spinner.cxx - Fl_Sys_Menu_Bar.cxx - Fl_System_Driver.cxx - Fl_Table.cxx - Fl_Table_Row.cxx - Fl_Tabs.cxx - Fl_Terminal.cxx - Fl_Text_Buffer.cxx - Fl_Text_Display.cxx - Fl_Text_Editor.cxx - Fl_Tile.cxx - Fl_Tiled_Image.cxx - Fl_Timeout.cxx - Fl_Tooltip.cxx - Fl_Tree.cxx - Fl_Tree_Item_Array.cxx - Fl_Tree_Item.cxx - Fl_Tree_Prefs.cxx - Fl_Valuator.cxx - Fl_Value_Input.cxx - Fl_Value_Output.cxx - Fl_Value_Slider.cxx - Fl_Widget.cxx - Fl_Widget_Surface.cxx - Fl_Window.cxx - Fl_Window_Driver.cxx - Fl_Window_fullscreen.cxx - Fl_Window_hotspot.cxx - Fl_Window_iconize.cxx - Fl_Wizard.cxx - Fl_XBM_Image.cxx - Fl_XPM_Image.cxx - Fl_abort.cxx - Fl_add_idle.cxx - Fl_arg.cxx - Fl_compose.cxx - Fl_display.cxx - Fl_get_system_colors.cxx - Fl_grab.cxx - Fl_lock.cxx - Fl_own_colormap.cxx - Fl_visual.cxx - filename_absolute.cxx - filename_expand.cxx - filename_ext.cxx - filename_isdir.cxx - filename_list.cxx - filename_match.cxx - filename_setext.cxx - fl_arc.cxx - fl_ask.cxx - fl_boxtype.cxx - fl_color.cxx - fl_contrast.cxx - fl_cursor.cxx - fl_curve.cxx - fl_diamond_box.cxx - fl_draw.cxx - fl_draw_arrow.cxx - fl_draw_pixmap.cxx - fl_encoding_latin1.cxx - fl_encoding_mac_roman.cxx - fl_engraved_label.cxx - fl_file_dir.cxx - fl_font.cxx - fl_gleam.cxx - fl_gtk.cxx - fl_labeltype.cxx - fl_open_uri.cxx - fl_oval_box.cxx - fl_overlay.cxx - fl_oxy.cxx - fl_plastic.cxx - fl_read_image.cxx - fl_rect.cxx - fl_round_box.cxx - fl_rounded_box.cxx - fl_set_font.cxx - fl_scroll_area.cxx - fl_shadow_box.cxx - fl_shortcut.cxx - fl_show_colormap.cxx - fl_string_functions.cxx - fl_symbols.cxx - fl_utf8.cxx - fl_vertex.cxx - print_button.cxx - screen_xywh.cxx -) - -if(FLTK_HAVE_CAIRO) # FLTK_OPTION_CAIRO_WINDOW or FLTK_OPTION_CAIRO_EXT - list(APPEND CPPFILES Fl_Cairo.cxx) -endif() - -# find all header files in includes directory -file(GLOB - HEADER_FILES - "../FL/*.[hH]" - "../FL/core/*.[hH]" -) - -# find all private header files in source directory "src/..." -file(GLOB - PRIVATE_HEADER_FILES - "*.[hH]" -) - -# find all private header files in source directory "src/..." -file(GLOB - PRIVATE_HEADER_FILES - "*.[hH]" -) - -# add generated header files in build directory -list(APPEND HEADER_FILES - ${CMAKE_CURRENT_BINARY_DIR}/../FL/fl_config.h - ${CMAKE_CURRENT_BINARY_DIR}/../config.h -) - -set(GL_HEADER_FILES) # FIXME: not (yet?) defined - -if(FLTK_USE_X11 AND NOT FLTK_OPTION_PRINT_SUPPORT) - set(PSFILES) -else() - set(PSFILES - drivers/PostScript/Fl_PostScript.cxx - drivers/PostScript/Fl_PostScript_image.cxx - ) -endif(FLTK_USE_X11 AND NOT FLTK_OPTION_PRINT_SUPPORT) - -set(DRIVER_FILES) - -if(FLTK_USE_X11 AND NOT FLTK_USE_WAYLAND) - - # X11 (including APPLE with X11) - - set(DRIVER_FILES - drivers/Posix/Fl_Posix_Printer_Driver.cxx - drivers/X11/Fl_X11_Screen_Driver.cxx - drivers/X11/Fl_X11_Window_Driver.cxx - drivers/Posix/Fl_Posix_System_Driver.cxx - drivers/Unix/Fl_Unix_System_Driver.cxx - drivers/Unix/Fl_Unix_Screen_Driver.cxx - drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx - drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx - drivers/X11/fl_X11_platform_init.cxx - Fl_x.cxx - fl_dnd_x.cxx - Fl_Native_File_Chooser_FLTK.cxx - Fl_Native_File_Chooser_GTK.cxx - Fl_get_key.cxx - ) - - list(APPEND DRIVER_FILES - Fl_Native_File_Chooser_Kdialog.cxx - Fl_Native_File_Chooser_Zenity.cxx) - - if(FLTK_USE_CAIRO) - list(APPEND DRIVER_FILES - drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx - drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx - ) - else() - if(USE_XFT) - list(APPEND DRIVER_FILES - drivers/Xlib/Fl_Xlib_Graphics_Driver_font_xft.cxx - ) - if(USE_PANGO) - list(APPEND DRIVER_FILES drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx) - endif(USE_PANGO) - else() - list(APPEND DRIVER_FILES - drivers/Xlib/Fl_Xlib_Graphics_Driver_font_x.cxx - ) - endif(USE_XFT) - list(APPEND DRIVER_FILES - drivers/Xlib/Fl_Xlib_Graphics_Driver.cxx - drivers/Xlib/Fl_Xlib_Graphics_Driver_arci.cxx - drivers/Xlib/Fl_Xlib_Graphics_Driver_color.cxx - drivers/Xlib/Fl_Xlib_Graphics_Driver_image.cxx - drivers/Xlib/Fl_Xlib_Graphics_Driver_line_style.cxx - drivers/Xlib/Fl_Xlib_Graphics_Driver_rect.cxx - drivers/Xlib/Fl_Xlib_Graphics_Driver_vertex.cxx - ) - endif(FLTK_USE_CAIRO) - - set(DRIVER_HEADER_FILES - drivers/Posix/Fl_Posix_System_Driver.H - drivers/X11/Fl_X11_Screen_Driver.H - drivers/X11/Fl_X11_Window_Driver.H - drivers/Xlib/Fl_Xlib_Graphics_Driver.H - drivers/Xlib/Fl_Font.H - drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H - drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H - drivers/Unix/Fl_Unix_System_Driver.H - ) - if(FLTK_USE_CAIRO) - set(DRIVER_HEADER_FILES ${DRIVER_HEADER_FILES} - drivers/Cairo/Fl_Cairo_Graphics_Driver.H - drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.H - ) - elseif(USE_PANGO) - set(DRIVER_HEADER_FILES ${DRIVER_HEADER_FILES} - drivers/Cairo/Fl_Cairo_Graphics_Driver.H - ) - endif(FLTK_USE_CAIRO) - -elseif(FLTK_USE_WAYLAND) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_BINARY_DIR}") - set(DRIVER_FILES - drivers/Posix/Fl_Posix_System_Driver.cxx - drivers/Posix/Fl_Posix_Printer_Driver.cxx - drivers/Unix/Fl_Unix_Screen_Driver.cxx - drivers/Wayland/Fl_Wayland_Screen_Driver.cxx - drivers/Wayland/Fl_Wayland_Window_Driver.cxx - drivers/Unix/Fl_Unix_System_Driver.cxx - drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx - drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx - drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx - drivers/Wayland/fl_wayland_clipboard_dnd.cxx - drivers/Wayland/fl_wayland_platform_init.cxx - drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx - Fl_Native_File_Chooser_FLTK.cxx - Fl_Native_File_Chooser_GTK.cxx - ) - - list(APPEND DRIVER_FILES - Fl_Native_File_Chooser_Kdialog.cxx - Fl_Native_File_Chooser_Zenity.cxx) - - if(FLTK_USE_X11) - list(APPEND DRIVER_FILES - drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx - drivers/X11/Fl_X11_Screen_Driver.cxx - drivers/X11/Fl_X11_Window_Driver.cxx - drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx - drivers/Xlib/Fl_Xlib_Image_Surface_Driver.cxx - Fl_x.cxx - fl_dnd_x.cxx - Fl_get_key.cxx - ) - endif(FLTK_USE_X11) - - set(DRIVER_HEADER_FILES - drivers/Posix/Fl_Posix_System_Driver.H - drivers/Wayland/Fl_Wayland_Screen_Driver.H - drivers/Wayland/Fl_Wayland_Window_Driver.H - drivers/Wayland/Fl_Wayland_Graphics_Driver.H - drivers/Cairo/Fl_Cairo_Graphics_Driver.H - drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.H - drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H - drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H - drivers/Unix/Fl_Unix_System_Driver.H - ) - -elseif(APPLE) - - # Apple Quartz - - set(DRIVER_FILES - drivers/Quartz/Fl_Quartz_Graphics_Driver.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_color.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_rect.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_font.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_vertex.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_image.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_arci.cxx - drivers/Quartz/Fl_Quartz_Graphics_Driver_line_style.cxx - drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.cxx - drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx - drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx - drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx - drivers/Posix/Fl_Posix_System_Driver.cxx - drivers/Darwin/Fl_Darwin_System_Driver.cxx - Fl_get_key_mac.cxx - drivers/Darwin/fl_macOS_platform_init.cxx - ) - set(DRIVER_HEADER_FILES - drivers/Posix/Fl_Posix_System_Driver.H - drivers/Darwin/Fl_Darwin_System_Driver.H - drivers/Cocoa/Fl_Cocoa_Screen_Driver.H - drivers/Cocoa/Fl_Cocoa_Window_Driver.H - drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H - drivers/Quartz/Fl_Quartz_Graphics_Driver.H - drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H - drivers/Quartz/Fl_Font.H - drivers/Quartz/Fl_Quartz_Image_Surface_Driver.H - ) - -else() - - # Windows (GDI) - - set(DRIVER_FILES - drivers/WinAPI/Fl_WinAPI_System_Driver.cxx - drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx - drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx - drivers/WinAPI/Fl_WinAPI_Printer_Driver.cxx - drivers/GDI/Fl_GDI_Graphics_Driver.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_arci.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_color.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_font.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_image.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_line_style.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_rect.cxx - drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx - drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx - drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx - Fl_win32.cxx - fl_dnd_win32.cxx - Fl_Native_File_Chooser_WIN32.cxx - Fl_get_key_win32.cxx - drivers/WinAPI/fl_WinAPI_platform_init.cxx - ) - set(DRIVER_HEADER_FILES - drivers/WinAPI/Fl_WinAPI_System_Driver.H - drivers/WinAPI/Fl_WinAPI_Screen_Driver.H - drivers/WinAPI/Fl_WinAPI_Window_Driver.H - drivers/GDI/Fl_GDI_Graphics_Driver.H - drivers/GDI/Fl_Font.H - drivers/GDI/Fl_GDI_Copy_Surface_Driver.H - drivers/GDI/Fl_GDI_Image_Surface_Driver.H - ) - - # Optional Pen/Tablet Support - - if(FLTK_HAVE_PEN_SUPPORT) - list(APPEND DRIVER_FILES - drivers/WinAPI/Fl_WinAPI_Pen_Driver.cxx - ) - endif(FLTK_HAVE_PEN_SUPPORT) - -endif(FLTK_USE_X11 AND NOT FLTK_USE_WAYLAND) - -# Common Pen/Tablet Support Files - -if(FLTK_HAVE_PEN_SUPPORT) - list(APPEND DRIVER_FILES - drivers/Base/Fl_Base_Pen_Events.cxx - ) - list(APPEND DRIVER_HEADER_FILES - drivers/Base/Fl_Base_Pen_Events.H - ) -endif(FLTK_HAVE_PEN_SUPPORT) - -source_group("Header Files" FILES ${HEADER_FILES}) -source_group("Private Header Files" FILES ${PRIVATE_HEADER_FILES}) -source_group("Driver Source Files" FILES ${DRIVER_FILES}) -source_group("Driver Header Files" FILES ${DRIVER_HEADER_FILES}) - -set(CPPFILES - ${CPPFILES} - ${DRIVER_FILES} -) - -if(FLTK_BUILD_FORMS) - set(FORMS_FILES - forms_compatibility.cxx - forms_bitmap.cxx - forms_free.cxx - forms_fselect.cxx - forms_pixmap.cxx - forms_timer.cxx - ) -else() - set(FORMS_FILES "") -endif() - -set(GLCPPFILES - Fl_Gl_Choice.cxx - Fl_Gl_Device_Plugin.cxx - Fl_Gl_Overlay.cxx - Fl_Gl_Window.cxx - freeglut_geometry.cxx - freeglut_stroke_mono_roman.cxx - freeglut_stroke_roman.cxx - freeglut_teapot.cxx - gl_draw.cxx - gl_start.cxx - glut_compatibility.cxx - glut_font.cxx -) - -set(GL_DRIVER_FILES - drivers/OpenGL/Fl_OpenGL_Display_Device.cxx - # the following file doesn't contribute any code: - # drivers/OpenGL/Fl_OpenGL_Graphics_Driver.cxx - drivers/OpenGL/Fl_OpenGL_Graphics_Driver_arci.cxx - drivers/OpenGL/Fl_OpenGL_Graphics_Driver_color.cxx - drivers/OpenGL/Fl_OpenGL_Graphics_Driver_font.cxx - drivers/OpenGL/Fl_OpenGL_Graphics_Driver_line_style.cxx - drivers/OpenGL/Fl_OpenGL_Graphics_Driver_rect.cxx - drivers/OpenGL/Fl_OpenGL_Graphics_Driver_vertex.cxx -) -if(FLTK_USE_WAYLAND) - set(GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx) - set(GL_DRIVER_HEADER_FILES drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H) - if(FLTK_USE_X11) - list(APPEND GL_DRIVER_FILES drivers/X11/Fl_X11_Gl_Window_Driver.cxx) - list(APPEND GL_DRIVER_HEADER_FILES drivers/X11/Fl_X11_Gl_Window_Driver.H) - endif(FLTK_USE_X11) -elseif(FLTK_USE_X11) - set(GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/X11/Fl_X11_Gl_Window_Driver.cxx) - set(GL_DRIVER_HEADER_FILES drivers/X11/Fl_X11_Gl_Window_Driver.H) -elseif(APPLE) - set(GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.mm) - set(GL_DRIVER_HEADER_FILES drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.H) -elseif(WIN32) - set(GL_DRIVER_FILES ${GL_DRIVER_FILES} drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx) - set(GL_DRIVER_HEADER_FILES drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.H) -endif(FLTK_USE_WAYLAND) - -set(GL_DRIVER_HEADER_FILES ${GL_DRIVER_FILES} - drivers/OpenGL/Fl_OpenGL_Display_Device.H - drivers/OpenGL/Fl_OpenGL_Graphics_Driver.H -) - -source_group("Driver Header Files" FILES ${GL_DRIVER_HEADER_FILES}) -source_group("Driver Source Files" FILES ${GL_DRIVER_FILES}) - -set(GLCPPFILES - ${GLCPPFILES} - ${GL_DRIVER_FILES} -) - -set(IMGCPPFILES - fl_images_core.cxx - fl_write_png.cxx - Fl_BMP_Image.cxx - Fl_File_Icon2.cxx - Fl_GIF_Image.cxx - Fl_Anim_GIF_Image.cxx - Fl_Help_Dialog.cxx - Fl_ICO_Image.cxx - Fl_JPEG_Image.cxx - Fl_PNG_Image.cxx - Fl_PNM_Image.cxx - Fl_Image_Reader.cxx - Fl_SVG_Image.cxx - nanosvg.cxx - drivers/SVG/Fl_SVG_File_Surface.cxx -) - -set(CFILES - flstring.c - numericsort.c - vsnprintf.c - xutf8/is_right2left.c - xutf8/is_spacing.c - xutf8/case.c -) - -if(FLTK_USE_X11) - list(APPEND CFILES - xutf8/keysym2Ucs.c - scandir_posix.c - ) - if(NOT USE_XFT) - list(APPEND CFILES - xutf8/utf8Utils.c - xutf8/utf8Input.c - ) - if(NOT APPLE) - list(APPEND CFILES xutf8/utf8Wrap.c) - endif(NOT APPLE) - endif(NOT USE_XFT) -endif(FLTK_USE_X11) - -if(FLTK_USE_WAYLAND) - set(IDIRS "${CMAKE_CURRENT_BINARY_DIR}") - set(CDEFS "_GNU_SOURCE;HAVE_MKOSTEMP") - if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "FreeBSD") - set(CDEFS "${CDEFS};HAVE_MEMFD_CREATE;HAVE_POSIX_FALLOCATE") - endif() - set(COPTS "-fPIC") - if(FLTK_USE_DBUS) - pkg_check_modules(DBUS IMPORTED_TARGET dbus-1) - if(DBUS_FOUND) - set(CDEFS "${CDEFS};HAS_DBUS") - set(IDIRS "${IDIRS};${DBUS_INCLUDE_DIRS}") - endif(DBUS_FOUND) - endif(FLTK_USE_DBUS) - if(USE_SYSTEM_LIBDECOR) - set(CDEFS "${CDEFS};USE_SYSTEM_LIBDECOR;LIBDECOR_PLUGIN_DIR=${LIBDECOR_PLUGIN_DIR}") - if(GTK_FOUND) - set(CDEFS "${CDEFS};HAVE_GTK") - endif(GTK_FOUND) - set_source_files_properties( - ${FLTK_SOURCE_DIR}/libdecor/build/fl_libdecor-plugins.c - ${FLTK_SOURCE_DIR}/libdecor/src/os-compatibility.c - ${FLTK_SOURCE_DIR}/libdecor/src/desktop-settings.c - PROPERTIES COMPILE_DEFINITIONS "${CDEFS}" - INCLUDE_DIRECTORIES "${IDIRS}" - COMPILE_OPTIONS "${COPTS}" - ) - else() - set(IDIRS "${IDIRS};${FLTK_SOURCE_DIR}/libdecor/src" - "${FLTK_SOURCE_DIR}/libdecor/src/plugins") - set(CDEFS "${CDEFS};USE_SYSTEM_LIBDECOR=0;LIBDECOR_PLUGIN_API_VERSION=1" - "LIBDECOR_PLUGIN_DIR=\"\"") - if(GTK_FOUND AND FLTK_USE_LIBDECOR_GTK) - set(CDEFS "${CDEFS};HAVE_GTK") - endif(GTK_FOUND AND FLTK_USE_LIBDECOR_GTK) - set_source_files_properties( - ${FLTK_SOURCE_DIR}/libdecor/build/fl_libdecor.c - ${FLTK_SOURCE_DIR}/libdecor/build/fl_libdecor-plugins.c - ${FLTK_SOURCE_DIR}/libdecor/src/os-compatibility.c - ${FLTK_SOURCE_DIR}/libdecor/src/desktop-settings.c - PROPERTIES - COMPILE_DEFINITIONS "${CDEFS}" - INCLUDE_DIRECTORIES "${IDIRS}" - COMPILE_OPTIONS "${COPTS}" - ) - endif(USE_SYSTEM_LIBDECOR) - set_source_files_properties( - ${FLTK_SOURCE_DIR}/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx - ${FLTK_SOURCE_DIR}/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx - PROPERTIES COMPILE_DEFINITIONS "USE_SYSTEM_LIBDECOR=${USE_SYSTEM_LIBDECOR}" - ) - - list(APPEND CFILES - scandir_posix.c - ../libdecor/src/desktop-settings.c - ../libdecor/src/os-compatibility.c - ../libdecor/build/fl_libdecor-plugins.c - ) - if(FLTK_USE_X11) - list(APPEND CFILES xutf8/keysym2Ucs.c) - endif(FLTK_USE_X11) - if(NOT USE_SYSTEM_LIBDECOR) - list(APPEND CFILES - ../libdecor/build/fl_libdecor.c - ../libdecor/src/plugins/common/libdecor-cairo-blur.c - ) - endif(NOT USE_SYSTEM_LIBDECOR) -endif(FLTK_USE_WAYLAND) - -if(WIN32) - list(APPEND CFILES - scandir_win32.c - ) -endif(WIN32) - -if(APPLE AND NOT FLTK_BACKEND_X11) - set(MMFILES - Fl_cocoa.mm - drivers/Cocoa/Fl_Cocoa_Printer_Driver.mm - Fl_Native_File_Chooser_MAC.mm - Fl_MacOS_Sys_Menu_Bar.mm - ) - if(FLTK_HAVE_PEN_SUPPORT) - list(APPEND MMFILES - drivers/Cocoa/Fl_Cocoa_Pen_Events.mm - ) - endif() - source_group("ObjC Source Files" FILES ${MMFILES}) -else() - set(MMFILES) -endif() - - -####################################################################### - -# prepare source files for shared and static FLTK libraries - -set(SHARED_FILES ${CPPFILES} ${MMFILES} ${CFILES} ${PSFILES}) -list(APPEND SHARED_FILES ${HEADER_FILES} ${PRIVATE_HEADER_FILES} ${DRIVER_HEADER_FILES}) - -set(STATIC_FILES ${SHARED_FILES}) - -# Visual Studio (MSVC) is known to need WinMain() and maybe BORLAND -# needs it as well, hence we include it on all Windows platforms. -# The GNU compilers (MinGW, MSYS2, Cygwin) disable compilation inside -# the source file which is what we finally want and need. - -if(WIN32) - list(APPEND STATIC_FILES fl_call_main.c) -endif() - -####################################################################### -# -# Prepare optional libs for shared and static FLTK libraries. -# -# Notes: -# - 'OPTIONAL_LIBS' is a CMake 'list' but must not contain arbitrary -# CMake targets because these targets would be propagated to -# consumer projects. The macro below simplifies adding link -# libraries of such targets to 'OPTIONAL_LIBS'. -# - 'OPTIONAL_INCLUDES' is a similar CMake list that defines additional -# include directories. -# -# This macro appends link libraries to 'OPTIONAL_LIBS' and include -# directories to 'OPTIONAL_INCLUDES'. -# -# Input: -# 'targets' may be a CMake list of targets or a single target. -# It must be quoted if multiple targets are to be added in -# one call (see examples below). -# -####################################################################### - -set(OPTIONAL_LIBS) -set(OPTIONAL_INCLUDES) - -macro(append_optional_libs targets) - foreach(_target ${targets}) - get_target_property(_link_libraries ${_target} INTERFACE_LINK_LIBRARIES) - if(_link_libraries) - list(APPEND OPTIONAL_LIBS ${_link_libraries}) - endif() - get_target_property(_include_dirs ${_target} INTERFACE_INCLUDE_DIRECTORIES) - if(_include_dirs) - list(APPEND OPTIONAL_INCLUDES ${_include_dirs}) - endif() - endforeach() - unset(_target) - unset(_link_libraries) - unset(_include_dirs) -endmacro() - -# Add the required properties for ${OPTIONAL_LIBS} to the given target. -# Note: we must use 'PUBLIC', see GitHub Issue #1173 - -macro(add_optional_libs target) - target_link_libraries (${target} PUBLIC ${OPTIONAL_LIBS}) - target_include_directories(${target} PUBLIC ${OPTIONAL_INCLUDES}) -endmacro() - -# Build the list of optional libs - -if(LIB_dl) - list(APPEND OPTIONAL_LIBS ${LIB_dl}) -endif(LIB_dl) - -if(USE_THREADS) - list(APPEND OPTIONAL_LIBS ${CMAKE_THREAD_LIBS_INIT}) -endif(USE_THREADS) - -if(FLTK_USE_X11) - list(APPEND OPTIONAL_LIBS ${X11_LIBRARIES}) - list(APPEND OPTIONAL_INCLUDES ${X11_INCLUDE_DIR}) -endif(FLTK_USE_X11) - -if(WIN32) - list(APPEND OPTIONAL_LIBS comctl32 ws2_32) - if(USE_GDIPLUS) - list(APPEND OPTIONAL_LIBS gdiplus) - endif() -endif(WIN32) - -if(HAVE_XINERAMA) - list(APPEND OPTIONAL_LIBS ${X11_Xinerama_LIB}) -endif(HAVE_XINERAMA) - -if(HAVE_XFIXES) - list(APPEND OPTIONAL_LIBS ${X11_Xfixes_LIB}) -endif(HAVE_XFIXES) - -if(HAVE_XCURSOR) - list(APPEND OPTIONAL_LIBS ${X11_Xcursor_LIB}) -endif(HAVE_XCURSOR) - -if(HAVE_XRENDER) - list(APPEND OPTIONAL_LIBS ${X11_Xrender_LIB}) -endif(HAVE_XRENDER) - -if(USE_PANGO) - list(APPEND OPTIONAL_LIBS ${HAVE_LIB_PANGO}) - append_optional_libs(PkgConfig::PANGOCAIRO) - list(APPEND OPTIONAL_LIBS ${HAVE_LIB_GOBJECT}) - if(USE_PANGOXFT) - append_optional_libs(PkgConfig::PANGOXFT) - endif(USE_PANGOXFT) -endif(USE_PANGO) - -if(USE_XFT) - list(APPEND OPTIONAL_LIBS ${X11_Xft_LIB}) - if(LIB_fontconfig) - list(APPEND OPTIONAL_LIBS ${LIB_fontconfig}) - endif(LIB_fontconfig) -endif(USE_XFT) - -if(UNIX AND FLTK_USE_WAYLAND) - pkg_get_variable(PROTOCOLS wayland-protocols pkgdatadir) - # replace "//" with "/" - string(REPLACE "//" "/" PROTOCOLS ${PROTOCOLS}) - - # The following variable is used for finding required files and to terminate - # the build if one or more files are not found. For user convenience - # the build is terminated after *all* files have been checked. - set(STOP_REQUIRED FALSE) - - set(INFILE ${PROTOCOLS}/stable/xdg-shell/xdg-shell.xml) - fl_check_required_file(STOP_REQUIRED ${INFILE}) - add_custom_command( - OUTPUT xdg-shell-protocol.c xdg-shell-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} xdg-shell-protocol.c - COMMAND wayland-scanner client-header ${INFILE} xdg-shell-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "xdg-shell-protocol.c") - list(APPEND SHARED_FILES "xdg-shell-protocol.c") - - if(NOT USE_SYSTEM_LIBDECOR) - set(INFILE ${PROTOCOLS}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml) - fl_check_required_file(STOP_REQUIRED ${INFILE}) - add_custom_command( - OUTPUT xdg-decoration-protocol.c xdg-decoration-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} xdg-decoration-protocol.c - COMMAND wayland-scanner client-header ${INFILE} xdg-decoration-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "xdg-decoration-protocol.c") - list(APPEND SHARED_FILES "xdg-decoration-protocol.c") - endif(NOT USE_SYSTEM_LIBDECOR) - - set(INFILE ${FLTK_SOURCE_DIR}/libdecor/build/gtk-shell.xml) - add_custom_command( - OUTPUT gtk-shell-protocol.c gtk-shell-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} gtk-shell-protocol.c - COMMAND wayland-scanner client-header ${INFILE} gtk-shell-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "gtk-shell-protocol.c") - list(APPEND SHARED_FILES "gtk-shell-protocol.c") - - set(INFILE ${PROTOCOLS}/unstable/text-input/text-input-unstable-v3.xml) - fl_check_required_file(STOP_REQUIRED ${INFILE}) - add_custom_command( - OUTPUT text-input-protocol.c text-input-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} text-input-protocol.c - COMMAND wayland-scanner client-header ${INFILE} text-input-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "text-input-protocol.c") - list(APPEND SHARED_FILES "text-input-protocol.c") - - if(HAVE_XDG_DIALOG) - set(INFILE ${PROTOCOLS}/staging/xdg-dialog/xdg-dialog-v1.xml) - add_custom_command( - OUTPUT xdg-dialog-protocol.c xdg-dialog-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} xdg-dialog-protocol.c - COMMAND wayland-scanner client-header ${INFILE} xdg-dialog-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "xdg-dialog-protocol.c") - list(APPEND SHARED_FILES "xdg-dialog-protocol.c") - endif() - - if(HAVE_CURSOR_SHAPE) - set(INFILE ${PROTOCOLS}/staging/cursor-shape/cursor-shape-v1.xml) - add_custom_command( - OUTPUT cursor-shape-protocol.c cursor-shape-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} cursor-shape-protocol.c - COMMAND wayland-scanner client-header ${INFILE} cursor-shape-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "cursor-shape-protocol.c") - list(APPEND SHARED_FILES "cursor-shape-protocol.c") -# File cursor-shape-protocol.c requires memory data allocated by the "Tablet" protocol -# but that protocol's header file is not necessary for the "cursor shape" protocol itself. - set(INFILE ${PROTOCOLS}/stable/tablet/tablet-v2.xml) - add_custom_command( - OUTPUT tablet-protocol.c # tablet-client-protocol.h - COMMAND wayland-scanner private-code ${INFILE} tablet-protocol.c -# COMMAND wayland-scanner client-header ${INFILE} tablet-client-protocol.h - DEPENDS ${INFILE} - VERBATIM - ) - list(APPEND STATIC_FILES "tablet-protocol.c") - list(APPEND SHARED_FILES "tablet-protocol.c") - endif() - - if(STOP_REQUIRED) - message(FATAL_ERROR "*** Terminating: one or more required file(s) were not found. ***") - endif() - - if(FLTK_USE_GL) - append_optional_libs("PkgConfig::WLD_EGL;PkgConfig::PKG_EGL") - endif(FLTK_USE_GL) - - if(USE_SYSTEM_LIBDECOR) - append_optional_libs(PkgConfig::SYSTEM_LIBDECOR) - elseif(GTK_FOUND AND FLTK_USE_LIBDECOR_GTK) - append_optional_libs(PkgConfig::GTK) - endif() - - append_optional_libs("PkgConfig::WLDCURSOR;PkgConfig::WLDCLIENT;PkgConfig::XKBCOMMON") - - if(FLTK_USE_DBUS AND DBUS_FOUND) - append_optional_libs(PkgConfig::DBUS) - endif() - -endif(UNIX AND FLTK_USE_WAYLAND) - -list(REMOVE_DUPLICATES OPTIONAL_LIBS) -list(REMOVE_DUPLICATES OPTIONAL_INCLUDES) - -####################################################################### - -fl_add_library(fltk STATIC "${STATIC_FILES}") -add_optional_libs(fltk) - -####################################################################### - -if(FLTK_BUILD_FORMS) - fl_add_library(fltk_forms STATIC "${FORMS_FILES}") - target_link_libraries(fltk_forms PUBLIC fltk::fltk) -endif() - -####################################################################### - -if(0) - message(STATUS "---------------------- fltk_images -----------------------") - fl_debug_var(FLTK_JPEG_LIBRARIES) - fl_debug_var(FLTK_PNG_LIBRARIES) - fl_debug_var(FLTK_ZLIB_LIBRARIES) - fl_debug_var(FLTK_IMAGE_LIBRARIES) - message(STATUS "---------------------- fltk_images -----------------------") -endif() - -fl_add_library(fltk_images STATIC "${IMGCPPFILES}") -target_link_libraries(fltk_images PUBLIC fltk::fltk) -target_link_libraries(fltk_images PRIVATE ${FLTK_IMAGE_LIBRARIES}) - -if(FLTK_USE_BUNDLED_JPEG) - target_include_directories(fltk_images PUBLIC - $ - $) -endif() - -if(FLTK_USE_BUNDLED_PNG) - target_include_directories(fltk_images PUBLIC - $ - $) -endif() - -if(FLTK_USE_BUNDLED_ZLIB) - target_include_directories(fltk_images PUBLIC - $ - $) -endif() - -####################################################################### - -if(FLTK_USE_GL) - fl_add_library(fltk_gl STATIC "${GLCPPFILES};${GL_HEADER_FILES};${GL_DRIVER_HEADER_FILES}") - target_link_libraries(fltk_gl PUBLIC ${OPENGL_LIBRARIES} fltk::fltk) - target_include_directories(fltk_gl PUBLIC ${OPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIRS}) - - # Add "optional libs" (FIXME: this can be optimized, we don't need *all* these libs here) - add_optional_libs(fltk_gl) - - if(FLTK_OPENGL_GLU_INCLUDE_DIR) - target_include_directories(fltk_gl PUBLIC ${FLTK_OPENGL_GLU_INCLUDE_DIR}) - endif() -endif(FLTK_USE_GL) - -####################################################################### -# Build shared libraries (optional) -####################################################################### -# Shared libraries, part 1: everything except Visual Studio (MSVC) -####################################################################### - -if(FLTK_BUILD_SHARED_LIBS AND NOT MSVC) - - fl_add_library(fltk SHARED "${SHARED_FILES}") - add_optional_libs(fltk-shared) - - ################################################################### - - if(FLTK_BUILD_FORMS) - fl_add_library(fltk_forms SHARED "${FORMS_FILES}") - target_link_libraries(fltk_forms-shared PUBLIC fltk::fltk-shared) - endif() - - ################################################################### - - fl_add_library(fltk_images SHARED "${IMGCPPFILES}") - target_link_libraries(fltk_images-shared PUBLIC fltk::fltk-shared) - - target_link_libraries(fltk_images PUBLIC ${FLTK_JPEG_LIBRARIES}) - target_link_libraries(fltk_images PUBLIC ${FLTK_PNG_LIBRARIES}) - target_link_libraries(fltk_images PUBLIC ${FLTK_IMAGE_LIBRARIES}) - - if(FLTK_USE_BUNDLED_JPEG) - target_include_directories(fltk_images-shared PUBLIC - $ - # $ - ) - endif() - - if(FLTK_USE_BUNDLED_JPEG) - target_link_libraries(fltk_images-shared PUBLIC fltk::jpeg-shared) - else() - target_link_libraries(fltk_images-shared PUBLIC ${JPEG_LIBRARIES}) - endif() - - if(FLTK_USE_BUNDLED_PNG) - target_link_libraries(fltk_images-shared PUBLIC fltk::png-shared) - else() - target_link_libraries(fltk_images-shared PUBLIC ${PNG_LIBRARIES}) - endif() - - ################################################################### - - if(FLTK_USE_GL) - fl_add_library(fltk_gl SHARED "${GLCPPFILES};${GL_HEADER_FILES};${GL_DRIVER_HEADER_FILES}") - target_link_libraries(fltk_gl-shared PUBLIC ${OPENGL_LIBRARIES} fltk::fltk-shared) - target_include_directories(fltk_gl-shared PUBLIC ${OPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIRS}) - - # Add "optional libs" (FIXME: this can be optimized, we don't need *all* these libs here) - add_optional_libs(fltk_gl-shared) - - if(FLTK_OPENGL_GLU_INCLUDE_DIR) - target_include_directories(fltk_gl-shared PUBLIC ${FLTK_OPENGL_GLU_INCLUDE_DIR}) - endif() - endif(FLTK_USE_GL) - -endif(FLTK_BUILD_SHARED_LIBS AND NOT MSVC) - -####################################################################### -# Shared libraries, part 2: Visual Studio (MSVC) -####################################################################### - -# Note to devs: As of June 2020 we can't build separate shared libs -# (DLL's) under Windows with Visual Studio (MSVC) but we can build one -# big DLL that comprises all FLTK and optional PNG, JPEG, and ZLIB libs. -# The reason is the common DLL linkage (FL_EXPORT) for all libs. This -# might be changed in the future but it would require a lot of work. -# AlbrechtS - -if(FLTK_BUILD_SHARED_LIBS AND MSVC) - - set(SOURCES ${SHARED_FILES} ${FORMS_FILES} ${IMGCPPFILES}) - if(OPENGL_FOUND) - list(APPEND SOURCES ${GLCPPFILES} ${GL_HEADER_FILES} ${GL_DRIVER_HEADER_FILES}) - endif(OPENGL_FOUND) - - fl_add_library(fltk SHARED "${SOURCES}") - add_optional_libs(fltk-shared) - - if(FLTK_USE_BUNDLED_JPEG) - target_link_libraries(fltk-shared PUBLIC fltk::jpeg-shared) - else() - target_link_libraries(fltk-shared PUBLIC ${FLTK_JPEG_LIBRARIES}) - endif() - - if(FLTK_USE_BUNDLED_PNG) - target_link_libraries(fltk-shared PUBLIC fltk::png-shared) - else() - target_link_libraries(fltk-shared PUBLIC ${FLTK_PNG_LIBRARIES}) - endif() - - if(OPENGL_FOUND) - target_link_libraries(fltk-shared PUBLIC ${OPENGL_LIBRARIES}) - target_include_directories(fltk-shared PUBLIC ${OPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIRS}) - if(FLTK_OPENGL_GLU_INCLUDE_DIR) - target_include_directories(fltk-shared PUBLIC ${FLTK_OPENGL_GLU_INCLUDE_DIR}) - endif() - endif(OPENGL_FOUND) - -endif(FLTK_BUILD_SHARED_LIBS AND MSVC) - -####################################################################### - -set(FLTK_LIBRARIES ${FLTK_LIBRARIES} PARENT_SCOPE) -set(FLTK_LIBRARIES_SHARED ${FLTK_LIBRARIES_SHARED} PARENT_SCOPE) diff --git a/src/Fl.cxx b/src/Fl.cxx index 399d8249b..07e107b1d 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -2572,73 +2572,16 @@ FL_EXPORT const char* fl_local_alt = Fl::system_driver()->alt_name(); FL_EXPORT const char* fl_local_ctrl = Fl::system_driver()->control_name(); /** - Convert Windows commandline arguments to UTF-8. - - \note This function does nothing on other (non-Windows) platforms, hence - you may call it on all platforms or only on Windows by using platform - specific code like '\#ifdef _WIN32' etc. - it's your choice. - Calling it on other platforms returns quickly w/o wasting much CPU time. - - This function must be called on Windows platforms in \c main() - before the array \c argv is used if your program uses any commandline - argument strings (these should be UTF-8 encoded). - This applies also to standard FLTK commandline arguments like - "-name" (class name) and "-title" (window title in the title bar). - - Unfortunately Windows \b neither provides commandline arguments in UTF-8 - encoding \b nor as Windows "Wide Character" strings in the standard - \c main() and/or the Windows specific \c WinMain() function. - - On Windows platforms (no matter which build system) this function calls - a Windows specific function to retrieve commandline arguments as Windows - "Wide Character" strings, converts these strings to an internally allocated - buffer (or multiple buffers) and returns the result in \c argv. - For implementation details please refer to the source code; however these - details may be changed in the future. - - Note that \c argv is provided by reference so it can be overwritten. - - In the recommended simple form the function overwrites the variable - \c argv and allocates a new array of strings pointed to by \c argv. - You may use this form on all platforms and it is as simple as adding - one line to old programs to make them work with international (UTF-8) - commandline arguments. + Ensure commandline arguments are UTF-8 encoded. - \code - int main(int argc, char **argv) { - Fl::args_to_utf8(argc, argv); // add this line - // ... use argc and argv, e.g. for commandline parsing - window->show(argc, argv); - return Fl::run(); - } - \endcode - - For an example see 'examples/howto-parse-args.cxx' in the FLTK sources. - - If you want to retain the original \c argc and \c argv variables the - following slightly longer and more complicated code works as well on - all platforms. + On X11 this is a no-op since arguments are already in the locale encoding + which is typically UTF-8 on modern systems. - \code - int main(int argc, char **argv) { - char **argvn = argv; // must copy argv to work on all platforms - int argcn = Fl::args_to_utf8(argc, argvn); - // ... use argcn and argvn, e.g. for commandline parsing - window->show(argcn, argvn); - return Fl::run(); - } - \endcode - - \param[in] argc used only on non-Windows platforms - \param[out] argv modified only on Windows platforms + \param[in] argc argument count + \param[out] argv argument array \returns argument count (always the same as argc) \since 1.4.0 - - \internal This function must not open the display, otherwise - commandline processing (e.g. by fluid) would open the display. - OTOH calling it when the display is opened wouldn't work either - for the same reasons ('fluid -c' doesn't open the display). */ int Fl::args_to_utf8(int argc, char ** &argv) { return Fl::system_driver()->args_to_utf8(argc, argv); diff --git a/src/Fl_Cairo.cxx b/src/Fl_Cairo.cxx index 1b830c045..20cb08b56 100644 --- a/src/Fl_Cairo.cxx +++ b/src/Fl_Cairo.cxx @@ -27,37 +27,9 @@ #ifdef FLTK_HAVE_CAIRO -// Define USE_MAC_OS for convenience (below). We use macOS specific features -// if USE_MAC_OS is defined, otherwise we're using X11 (XQuartz) on macOS - -#if defined __APPLE__ && !defined(FLTK_USE_X11) -#define USE_MAC_OS -#include -#endif - #include #include - -// Cairo is currently supported for the following platforms: -// Windows, macOS (Apple Quartz), X11, Wayland - -#if defined(_WIN32) // Windows -# include -#elif defined(FLTK_USE_WAYLAND) // Wayland or hybrid -# include "../src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H" -# include "../src/drivers/Wayland/Fl_Wayland_Window_Driver.H" -# if defined(FLTK_USE_X11) -# include -# else - static void *fl_gc = NULL; -# endif -#elif defined(FLTK_USE_X11) // X11 -# include -#elif defined(__APPLE__) // macOS -# include -#else -# error Cairo is not supported on this platform. -#endif +#include // static initialization @@ -140,24 +112,13 @@ void Fl_Cairo_State::autolink(bool b) { \note Only available when CMake option '-D FLTK_OPTION_CAIRO_WINDOW' is set. \return The valid cairo_t *cairo context associated to this window. - \retval NULL if \a wi is NULL or maybe with GL windows under Wayland + \retval NULL if \a wi is NULL */ cairo_t *Fl::cairo_make_current(Fl_Window *wi) { if (!wi) return NULL; cairo_t *cairo_ctxt; -#if defined(FLTK_USE_WAYLAND) - if (fl_wl_display()) { // true means using wayland backend - struct wld_window *xid = fl_wl_xid(wi); - if (!xid || !xid->buffer) - return NULL; // this may happen with GL windows or if window is not shown - cairo_ctxt = xid->buffer->draw_buffer.cairo_; - Fl::Private::cairo_state_.cc(cairo_ctxt, false); - return cairo_ctxt; - } -#endif - if (fl_gc == 0) { // means remove current cc Fl::cairo_cc(0); // destroy any previous cc Fl::Private::cairo_state_.window(0); @@ -168,93 +129,23 @@ cairo_t *Fl::cairo_make_current(Fl_Window *wi) { if (fl_gc == Fl::Private::cairo_state_.gc() && fl_xid(wi) == (Window)Fl::Private::cairo_state_.window()) return Fl::cairo_cc(); - // Scale the Cairo context appropriately. This is platform dependent - -#if !defined(USE_MAC_OS) - float scale = Fl::screen_scale(wi->screen_num()); // get the screen scaling factor -#endif - -#if defined(FLTK_USE_X11) + // Scale the Cairo context appropriately + float scale = Fl::screen_scale(wi->screen_num()); cairo_ctxt = Fl::Private::cairo_make_current(0, wi->w() * scale, wi->h() * scale); -#else - // on macOS, scaling is done before by Fl_Window::make_current(), on Windows, the size is not used - cairo_ctxt = Fl::Private::cairo_make_current(fl_gc, wi->w(), wi->h()); -#endif - Fl::Private::cairo_state_.window((void *)fl_xid(wi)); - -#if !defined(USE_MAC_OS) cairo_scale(cairo_ctxt, scale, scale); -#endif return cairo_ctxt; } /* - Creates transparently a cairo_surface_t object. - gc is an HDC context in Windows, a CGContext* in Quartz, and - a display on X11 (not used on this platform) + Creates a cairo_surface_t object using X11/Xlib. */ static cairo_surface_t *cairo_create_surface(void *gc, int W, int H) { -#if defined(FLTK_USE_X11) return cairo_xlib_surface_create(fl_display, fl_window, fl_visual->visual, W, H); -#elif defined(FLTK_USE_WAYLAND) - return NULL; -#elif defined(_WIN32) - return cairo_win32_surface_create((HDC)gc); -#elif defined(__APPLE__) - return cairo_quartz_surface_create_for_cg_context((CGContextRef)gc, W, H); -#else -#error Cairo is not supported on this platform. -#endif } -#if 0 // this non-public function appears not to be used anywhere in FLTK -/** - Creates a Cairo context from a \a gc only, gets its window size or - offscreen size if fl_window is null. - - \note Only available if CMake FLTK_OPTION_CAIRO_WINDOW is enabled. -*/ -cairo_t *Fl::Private::cairo_make_current(void *gc) { - int W = 0, H = 0; -#if defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND) - // FIXME X11 get W,H - // gc will be the window handle here - // # warning FIXME get W,H for cairo_make_current(void*) -#elif defined(__APPLE__) - if (fl_window) { - W = Fl_Window::current()->w(); - H = Fl_Window::current()->h(); - } else { - W = CGBitmapContextGetWidth(fl_gc); - H = CGBitmapContextGetHeight(fl_gc); - } -#elif defined(_WIN32) - // we don't need any W,H for Windows -#else -#error Cairo is not supported on this platform. -#endif - - if (!gc) { - Fl::cairo_cc(0); - cairo_state_.gc(0); // keep track for next time - return 0; - } - if (gc == Fl::Private::cairo_state_.gc() && - fl_window == (Window)Fl::Private::cairo_state_.window() && - cairo_state_.cc() != 0) - return Fl::cairo_cc(); - cairo_state_.gc(fl_gc); // keep track for next time - cairo_surface_t *s = cairo_create_surface(gc, W, H); - cairo_t *c = cairo_create(s); - cairo_surface_destroy(s); - cairo_state_.cc(c); - return c; -} -#endif - /** Creates a Cairo context from a \p gc and the given size. @@ -269,29 +160,14 @@ cairo_t *Fl::Private::cairo_make_current(void *gc, int W, int H) { // we need to (re-)create a fresh cc ... cairo_state_.gc(gc); // keep track for next time cairo_surface_t *s = cairo_create_surface(gc, W, H); - -#if defined(USE_MAC_OS) && defined(FLTK_HAVE_CAIROEXT) - CGAffineTransform at = CGContextGetCTM((CGContextRef)gc); - CGContextSaveGState((CGContextRef)gc); - CGContextConcatCTM((CGContextRef)gc, CGAffineTransformInvert(at)); -#endif - cairo_t *c = cairo_create(s); - -#if defined(USE_MAC_OS) && defined(FLTK_HAVE_CAIROEXT) - CGContextRestoreGState((CGContextRef)gc); -#endif - cairo_state_.cc(c); // and purge any previously owned context cairo_surface_destroy(s); return c; } /** Flush Cairo drawings on Cairo context \p c. - This is \b required on Windows if you use the Cairo context provided - by the "Cairo autolink" option. Call this when all your drawings on - the Cairo context are finished. This is maybe not necessary on other - platforms than Windows but it does no harm if you call it always. + Call this when all your drawings on the Cairo context are finished. You don't need to use this if you use an Fl_Cairo_Window which does this automatically after the draw callback returns. @@ -321,7 +197,7 @@ cairo_t *Fl::Private::cairo_make_current(void *gc, int W, int H) { \see Fl::cairo_make_current(Fl_Window*); */ FL_EXPORT extern void Fl::cairo_flush(cairo_t *c) { - // flush Cairo drawings: necessary at least for Windows + // flush Cairo drawings cairo_surface_t *s = cairo_get_target(c); cairo_surface_flush(s); } diff --git a/src/Fl_MacOS_Sys_Menu_Bar.mm b/src/Fl_MacOS_Sys_Menu_Bar.mm deleted file mode 100644 index f41db31b4..000000000 --- a/src/Fl_MacOS_Sys_Menu_Bar.mm +++ /dev/null @@ -1,785 +0,0 @@ -// -// MacOS system menu bar widget for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2021 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#if defined(__APPLE__) - -#include -#include -#include "drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H" -#include "flstring.h" -#include -#include -#include -#include "Fl_System_Driver.H" - -#import // keep this after include of Fl_MacOS_Sys_Menu_Bar_Driver.H because of check() conflict - -typedef const Fl_Menu_Item *pFl_Menu_Item; - -static Fl_Menu_Bar *custom_menu; -static NSString *localized_Window = nil; - -static char *remove_ampersand(const char *s); -extern void (*fl_lock_function)(); -extern void (*fl_unlock_function)(); - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 -static void previous_tab_cb(Fl_Widget *, void *data); -static void next_tab_cb(Fl_Widget *, void *data); -static void move_tab_cb(Fl_Widget *, void *data); -static void merge_all_windows_cb(Fl_Widget *, void *data); -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 -const NSInteger NSControlStateValueOn = NSOnState; -const NSInteger NSControlStateValueOff = NSOffState; -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -const NSUInteger NSEventModifierFlagCommand = NSCommandKeyMask; -const NSUInteger NSEventModifierFlagOption = NSAlternateKeyMask; -const NSUInteger NSEventModifierFlagControl = NSControlKeyMask; -const NSUInteger NSEventModifierFlagShift = NSShiftKeyMask; -#endif - -void Fl_MacOS_Sys_Menu_Bar_Driver::draw() { - bar->deactivate(); // prevent Fl_Sys_Menu_Bar object from receiving events -} - -Fl_MacOS_Sys_Menu_Bar_Driver* Fl_MacOS_Sys_Menu_Bar_Driver::driver() { - static Fl_MacOS_Sys_Menu_Bar_Driver *once = new Fl_MacOS_Sys_Menu_Bar_Driver(); - if (driver_ != once) { - if (driver_) { - once->bar = driver_->bar; - delete driver_; - } - driver_ = once; - if (driver_->bar) driver_->update(); - } - return once; -} - -/* Class FLMenuItem, derived from NSMenuItem, associates any item of the macOS system menu - with a corresponding Fl_Menu_Item as follows: - - if the system item's tag is >= 0, fl_sys_menu_bar->menu() + tag is the address - of the relevant Fl_Menu_Item; - - otherwise, the system item's representedObject is the Fl_Menu_Item's address. - This allows the MacOS system menu to use the same Fl_Menu_Item's as those used by FLTK menus, - the address of which can be relocated by the FLTK menu logic. - The "representedObject" is used for non-relocatable Fl_Menu_Item's associated to FL_SUBMENU_POINTER. - Sending the getFlItem message to a macOS system menu item (of class FLMenuItem) returns the address - of the relevant Fl_Menu_Item. -*/ - -// Apple App Menu -const char *Fl_Mac_App_Menu::about = "About %@"; -const char *Fl_Mac_App_Menu::print = "Print Front Window & Titlebar"; -const char *Fl_Mac_App_Menu::print_no_titlebar = "Print Front Window"; -const char *Fl_Mac_App_Menu::toggle_print_titlebar = "Toggle printing of titlebar"; -const char *Fl_Mac_App_Menu::services = "Services"; -const char *Fl_Mac_App_Menu::hide = "Hide %@"; -const char *Fl_Mac_App_Menu::hide_others = "Hide Others"; -const char *Fl_Mac_App_Menu::show = "Show All"; -const char *Fl_Mac_App_Menu::quit = "Quit %@"; - - -@interface FLMenuItem : NSMenuItem { -} -- (const Fl_Menu_Item*) getFlItem; -- (void) itemCallback:(Fl_Menu_*)menu; -- (void) doCallback; -- (void) customCallback; -- (void) directCallback; -- (void) setKeyEquivalentModifierMask:(int)value; -- (void) setFltkShortcut:(int)key; -+ (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu action:(SEL)selector; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 -- (BOOL)validateMenuItem:(NSMenuItem *)item; -#endif -@end - -@implementation FLMenuItem -- (const Fl_Menu_Item*) getFlItem -// returns the Fl_Menu_Item corresponding to this system menu item -{ - NSInteger tag = [self tag]; - if (tag >= 0) return fl_sys_menu_bar->menu() + tag; - return *(const Fl_Menu_Item**)[(NSData*)[self representedObject] bytes]; -} -- (void) itemCallback:(Fl_Menu_*)menu -{ - const Fl_Menu_Item *item = [self getFlItem]; - menu->picked(item); - Fl::flush(); - if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol - [self setState:(item->value() ? NSControlStateValueOn : NSControlStateValueOff)]; - } - else if ( item->flags & FL_MENU_RADIO ) { // update the menu radio symbols - NSMenu* this_menu = [self menu]; - NSInteger flRank = [this_menu indexOfItem:self]; - NSInteger last = [this_menu numberOfItems] - 1; - int from = (int)flRank; - while(from > 0) { - if ([[this_menu itemAtIndex:from-1] isSeparatorItem]) break; - item = [(FLMenuItem*)[this_menu itemAtIndex:from-1] getFlItem]; - if ( !(item->flags & FL_MENU_RADIO) ) break; - from--; - } - int to = (int)flRank; - while (to < last) { - if ([[this_menu itemAtIndex:to+1] isSeparatorItem]) break; - item = [(FLMenuItem*)[this_menu itemAtIndex:to+1] getFlItem]; - if (!(item->flags & FL_MENU_RADIO)) break; - to++; - } - for(int i = from; i <= to; i++) { - NSMenuItem *nsitem = [this_menu itemAtIndex:i]; - [nsitem setState:(nsitem != self ? NSControlStateValueOff : NSControlStateValueOn)]; - } - } -} -- (void) doCallback -{ - fl_lock_function(); - [self itemCallback:fl_sys_menu_bar]; - fl_unlock_function(); -} -- (void) customCallback -{ - fl_lock_function(); - [self itemCallback:custom_menu]; - fl_unlock_function(); -} -- (void) directCallback -{ - fl_lock_function(); - Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes]; - if ( item && item->callback() ) item->do_callback(NULL, FL_REASON_SELECTED); - fl_unlock_function(); -} -- (void) setKeyEquivalentModifierMask:(int)value -{ - NSUInteger macMod = 0; - if ( value & FL_META ) macMod = NSEventModifierFlagCommand; - if ( value & FL_SHIFT || (value > 0 && value < 127 && isupper(value)) ) macMod |= NSEventModifierFlagShift; - if ( value & FL_ALT ) macMod |= NSEventModifierFlagOption; - if ( value & FL_CTRL ) macMod |= NSEventModifierFlagControl; - [super setKeyEquivalentModifierMask:macMod]; -} -- (void) setFltkShortcut:(int)key -{ - // Separate key and modifier - int mod = key; - mod &= ~FL_KEY_MASK; // modifier(s) - key &= FL_KEY_MASK; // key - unichar mac_key = (unichar)key; - if ( (key >= (FL_F+1)) && (key <= FL_F_Last) ) { // Handle function keys - int fkey_num = (key - FL_F); // 1,2.. - mac_key = NSF1FunctionKey + fkey_num - 1; - } else if (key == FL_Escape) { - mac_key = 27; - } else if (key == FL_Tab) { - mac_key = NSTabCharacter; - } else if (key == FL_Enter) { - mac_key = 0x0d; - } else if (key == FL_BackSpace) { - mac_key = NSBackspaceCharacter; - } else if (key == FL_Delete) { - mac_key = NSDeleteCharacter; - } else if (key == FL_Up) { - mac_key = NSUpArrowFunctionKey; - } else if (key == FL_Down) { - mac_key = NSDownArrowFunctionKey; - } else if (key == FL_Left) { - mac_key = NSLeftArrowFunctionKey; - } else if (key == FL_Right) { - mac_key = NSRightArrowFunctionKey; - } else if (key == FL_Page_Up) { - mac_key = NSPageUpFunctionKey; - } else if (key == FL_Page_Down) { - mac_key = NSPageDownFunctionKey; - } else if (key == FL_KP_Enter) { - mac_key = 0x2324; // "⌤" U+2324 - } else if (key == FL_Home) { - mac_key = NSHomeFunctionKey; - } else if (key == FL_End) { - mac_key = NSEndFunctionKey; - } - [self setKeyEquivalent:[NSString stringWithCharacters:&mac_key length:1]]; - [self setKeyEquivalentModifierMask:mod]; -} -+ (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu action:(SEL)selector -{ - char *name = remove_ampersand(mitem->label()); - NSString *title = NSLocalizedString([NSString stringWithUTF8String:name], nil); - free(name); - FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:title - action:selector - keyEquivalent:@""]; - if (mitem->labelfont() & FL_BOLD) { - NSFont *boldFont = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]; - NSAttributedString *attributed = - [ [ [NSAttributedString alloc] initWithString:title - attributes:@{ NSFontAttributeName: boldFont }] - autorelease]; - item.attributedTitle = attributed; - } - // >= 0 if mitem is in the menu items of fl_sys_menu_bar, -1 if not - NSInteger index = (fl_sys_menu_bar ? fl_sys_menu_bar->find_index(mitem) : -1); - [item setTag:index]; - if (index < 0) { - NSData *pointer = [[NSData alloc] initWithBytes:&mitem length:sizeof(Fl_Menu_Item*)]; - [item setRepresentedObject:pointer]; - [pointer release];//pointer will dealloc each time item dealloc's - } - [menu addItem:item]; - [item setTarget:item]; - int retval = (int)[menu indexOfItem:item]; - [item release]; - return retval; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 -- (BOOL)validateMenuItem:(NSMenuItem *)item { - // return YES for all but items of the Window menu - if (fl_mac_os_version < 101200 || - Fl_Sys_Menu_Bar::window_menu_style() <= Fl_Sys_Menu_Bar::tabbing_mode_none || - [item hasSubmenu]) return YES; - NSString *title = [[item parentItem] title]; // 10.6 - if (!title || !localized_Window || [title compare:localized_Window] != NSOrderedSame) return YES; - const Fl_Menu_Item *flitem = [(FLMenuItem*)item getFlItem]; - Fl_Callback *item_cb = flitem->callback(); - if (item_cb == previous_tab_cb || item_cb == next_tab_cb || item_cb == move_tab_cb) { - // is the current window tabbed? - Fl_Window *win = Fl::first_window(); - NSWindow *main = win ? (NSWindow*)fl_xid(win) : nil; - return (main && [main tabbedWindows] != nil); - } else if (item_cb == merge_all_windows_cb) { - // is there any untabbed, tabbable window? - int total = 0, untabbed = 0; - while ((++flitem)->label()) { - total++; - NSWindow *nsw = (NSWindow*)fl_xid( (Fl_Window*)flitem->user_data() ); - if (![nsw tabbedWindows] && [nsw tabbingMode] != NSWindowTabbingModeDisallowed) { - untabbed++; - } - } - return (untabbed > 0 && total >= 2); - } - return YES; -} -#endif -@end - - -void Fl_MacOS_Sys_Menu_Bar_Driver::about( Fl_Callback *cb, void *user_data) -{ - Fl_Menu_Item aboutItem; - memset(&aboutItem, 0, sizeof(Fl_Menu_Item)); - aboutItem.callback(cb); - aboutItem.user_data(user_data); - NSMenu *appleMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; - CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]); - [appleMenu removeItemAtIndex:0]; - FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname - action:@selector(directCallback) - keyEquivalent:@""] autorelease]; - NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)]; - [item setRepresentedObject:pointer]; - [appleMenu insertItem:item atIndex:0]; - CFRelease(cfname); - [item setTarget:item]; -} - -/* - * Set a shortcut for an Apple menu item using the FLTK shortcut descriptor. - */ -static void setMenuShortcut( NSMenu* mh, int miCnt, const Fl_Menu_Item *m ) -{ - if ( !m->shortcut_ ) - return; - if ( m->flags & FL_SUBMENU ) - return; - if ( m->flags & FL_SUBMENU_POINTER ) - return; - FLMenuItem* menuItem = (FLMenuItem*)[mh itemAtIndex:miCnt]; - [menuItem setFltkShortcut:(m->shortcut_)]; -} - - -/* - * Set the Toggle and Radio flag based on FLTK flags - */ -static void setMenuFlags( NSMenu* mh, int miCnt, const Fl_Menu_Item *m ) -{ - if ( m->flags & FL_MENU_TOGGLE ) - { - NSMenuItem *menuItem = [mh itemAtIndex:miCnt]; - [menuItem setState:(m->flags & FL_MENU_VALUE ? NSControlStateValueOn : NSControlStateValueOff)]; - } - else if ( m->flags & FL_MENU_RADIO ) { - NSMenuItem *menuItem = [mh itemAtIndex:miCnt]; - [menuItem setState:(m->flags & FL_MENU_VALUE ? NSControlStateValueOn : NSControlStateValueOff)]; - } -} - -static char *remove_ampersand(const char *s) -{ - char *ret = fl_strdup(s); - const char *p = s; - char *q = ret; - while(*p != 0) { - if (p[0]=='&') { - if (p[1]=='&') { - *q++ = '&'; p+=2; - } else { - p++; - } - } else { - *q++ = *p++; - } - } - *q = 0; - return ret; -} - - -/* - * create a sub menu for a specific menu handle - */ -static void createSubMenu( NSMenu *mh, pFl_Menu_Item &mm, const Fl_Menu_Item *mitem, SEL selector) -{ - NSMenu *submenu; - int miCnt, flags; - - if (mitem) { - NSMenuItem *menuItem; - char *ts = remove_ampersand(mitem->text); - NSString *title = NSLocalizedString([NSString stringWithUTF8String:ts], nil); - free(ts); - submenu = [[NSMenu alloc] initWithTitle:(NSString*)title]; - [submenu setAutoenablesItems:NO]; - - int cnt; - cnt = (int)[mh numberOfItems]; - cnt--; - menuItem = [mh itemAtIndex:cnt]; - if (mitem->labelfont() & FL_BOLD) { - NSFont *boldFont = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]; - NSAttributedString *attributed = - [ [ [NSAttributedString alloc] initWithString:title - attributes:@{ NSFontAttributeName: boldFont }] - autorelease]; - menuItem.attributedTitle = attributed; - } - [menuItem setSubmenu:submenu]; - [submenu release]; - } else submenu = mh; - - while ( mm->text ) { - if (!mm->visible() ) { // skip invisible items and submenus - mm = mm->next(0); - continue; - } - miCnt = [FLMenuItem addNewItem:mm menu:submenu - action:( (mm->flags & (FL_SUBMENU+FL_SUBMENU_POINTER) && !mm->callback()) ? nil : selector) - ]; - setMenuFlags( submenu, miCnt, mm ); - setMenuShortcut( submenu, miCnt, mm ); - if (mitem && (mm->flags & FL_MENU_INACTIVE || mitem->flags & FL_MENU_INACTIVE)) { - NSMenuItem *item = [submenu itemAtIndex:miCnt]; - [item setEnabled:NO]; - } - flags = mm->flags; - if ( mm->flags & FL_SUBMENU ) - { - mm++; - createSubMenu( submenu, mm, mm - 1, selector); - } - else if ( mm->flags & FL_SUBMENU_POINTER ) - { - const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_; - createSubMenu( submenu, smm, mm, selector); - } - if ( flags & FL_MENU_DIVIDER ) { - [submenu addItem:[NSMenuItem separatorItem]]; - } - mm++; - } -} - - -/* - * convert a complete Fl_Menu_Item array into a series of menus in the top menu bar - * ALL PREVIOUS SYSTEM MENUS, EXCEPT THE APPLICATION MENU, ARE REPLACED BY THE NEW DATA - */ -static void convertToMenuBar(const Fl_Menu_Item *mm) -{ - NSMenu *fl_system_menu = [NSApp mainMenu]; - int count;//first, delete all existing system menus - count = (int)[fl_system_menu numberOfItems]; - for(int i = count - 1; i > 0; i--) { - [fl_system_menu removeItem:[fl_system_menu itemAtIndex:i]]; - } - if (mm) createSubMenu(fl_system_menu, mm, NULL, @selector(doCallback)); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (localized_Window) { - NSMenuItem *item = [fl_system_menu itemWithTitle:localized_Window]; - if (item) [[item submenu] setAutoenablesItems:YES]; - } -#endif -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::update() -{ - convertToMenuBar(bar->Fl_Menu_::menu()); -} - - -static int process_sys_menu_shortcuts(int event) -{ - if (event != FL_SHORTCUT || !fl_sys_menu_bar || Fl::modal()) return 0; - // is the last event the shortcut of an item of the fl_sys_menu_bar menu ? - const Fl_Menu_Item *item = fl_sys_menu_bar->menu()->test_shortcut(); - if (!item) return 0; - if (item->visible()) // have the system menu process the shortcut, highlighting the corresponding menu - [[NSApp mainMenu] performKeyEquivalent:[NSApp currentEvent]]; - else // have FLTK process the shortcut associated to an invisible Fl_Menu_Item - fl_sys_menu_bar->picked(item); - return 1; -} - -Fl_MacOS_Sys_Menu_Bar_Driver::Fl_MacOS_Sys_Menu_Bar_Driver() : Fl_Sys_Menu_Bar_Driver() -{ - window_menu_items = NULL; - first_window_menu_item = 0; - Fl::add_handler(process_sys_menu_shortcuts); -} - -Fl_MacOS_Sys_Menu_Bar_Driver::~Fl_MacOS_Sys_Menu_Bar_Driver() -{ - Fl::remove_handler(process_sys_menu_shortcuts); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::menu(const Fl_Menu_Item *m) -{ - fl_open_display(); - bar->Fl_Menu_Bar::menu( m ); - convertToMenuBar(m); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::clear() -{ - bar->Fl_Menu_::clear(); - convertToMenuBar(NULL); -} - -int Fl_MacOS_Sys_Menu_Bar_Driver::clear_submenu(int index) -{ - int retval = bar->Fl_Menu_::clear_submenu(index); - if (retval != -1) update(); - return retval; -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::remove(int index) -{ - bar->Fl_Menu_::remove(index); - update(); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::replace(int index, const char *name) -{ - bar->Fl_Menu_::replace(index, name); - update(); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::mode(int i, int fl) { - bar->Fl_Menu_::mode(i, fl); - update(); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::shortcut (int i, int s) { - bar->Fl_Menu_Bar::shortcut(i, s); - update(); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::setonly (Fl_Menu_Item *item) { - bar->Fl_Menu_::setonly(item); - update(); -} - -int Fl_MacOS_Sys_Menu_Bar_Driver::add(const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) -{ - fl_open_display(); - int index = bar->Fl_Menu_::add(label, shortcut, cb, user_data, flags); - update(); - return index; -} - -int Fl_MacOS_Sys_Menu_Bar_Driver::add(const char* str) -{ - fl_open_display(); - int index = bar->Fl_Menu_::add(str); - update(); - return index; -} - -int Fl_MacOS_Sys_Menu_Bar_Driver::insert(int index, const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) -{ - fl_open_display(); - int menu_index = bar->Fl_Menu_::insert(index, label, shortcut, cb, user_data, flags); - update(); - return menu_index; -} - -/** \class Fl_Mac_App_Menu - Mac OS-specific class allowing to customize and localize the application menu. - - The public class attributes are used to build the application menu. They can be localized - at run time to any UTF-8 text by placing instructions such as this before fl_open_display() - gets called: - \verbatim - Fl_Mac_App_Menu::print = "Imprimer la fenêtre"; - \endverbatim - \see \ref osissues_macos for another way to localization. - */ - -void Fl_Mac_App_Menu::custom_application_menu_items(const Fl_Menu_Item *m) -{ - fl_open_display(); // create the system menu, if needed - custom_menu = new Fl_Menu_Bar(0,0,0,0); - custom_menu->menu(m); - NSMenu *menu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; // the application menu - NSInteger to_index; - if ([[menu itemAtIndex:2] action] != @selector(printPanel)) { // the 'Print' item was removed - [menu insertItem:[NSMenuItem separatorItem] atIndex:1]; - to_index = 2; - } else to_index = 5; // after the "Print Front Window/Toggle" items and the separator - NSInteger count = [menu numberOfItems]; - createSubMenu(menu, m, NULL, @selector(customCallback)); // add new items at end of application menu - NSInteger count2 = [menu numberOfItems]; - for (NSInteger i = count; i < count2; i++) { // move new items to their desired position in application menu - NSMenuItem *item = [menu itemAtIndex:i]; - [item retain]; - [menu removeItemAtIndex:i]; - [menu insertItem:item atIndex:to_index++]; - [item release]; - } -} - -static void minimize_win_cb(Fl_Widget *, void *data) -{ - [[NSApp mainWindow] miniaturize:nil]; -} - -static void window_menu_cb(Fl_Widget *, void *data) -{ - if (data) ((Fl_Window*)data)->show(); -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - -static void previous_tab_cb(Fl_Widget *, void *data) -{ - [[NSApp mainWindow] selectPreviousTab:nil]; -} - -static void next_tab_cb(Fl_Widget *, void *data) -{ - [[NSApp mainWindow] selectNextTab:nil]; -} - -static void move_tab_cb(Fl_Widget *, void *data) -{ - [[NSApp mainWindow] moveTabToNewWindow:nil]; -} - -static void merge_all_windows_cb(Fl_Widget *, void *) -{ - Fl_Window *first = Fl::first_window(); - while (first && (first->parent() || !first->border())) - first = Fl::next_window(first); - if (first) { - [(NSWindow*)fl_xid(first) mergeAllWindows:nil]; - } -} - -#endif - - -static bool window_menu_installed = false; -static int window_menu_items_count = 0; - -void Fl_MacOS_Sys_Menu_Bar_Driver::create_window_menu(void) -{ - if (window_menu_style() == Fl_Sys_Menu_Bar::no_window_menu) return; - if (window_menu_installed) return; - window_menu_installed = true; - int rank = 0; - if (fl_sys_menu_bar && fl_sys_menu_bar->menu()) { - if (fl_sys_menu_bar->find_index("Window") >= 0) { // there's already a "Window" menu -> don't create another - window_menu_style_ = Fl_Sys_Menu_Bar::no_window_menu; - return; - } - // put the Window menu last in menu bar or before Help if it's present - const Fl_Menu_Item *item = fl_sys_menu_bar->menu(); - while (item->label() && strcmp(item->label(), "Help") != 0) { - item = item->next(); - } - rank = fl_sys_menu_bar->find_index(item); - } else if (!fl_sys_menu_bar) { - fl_open_display(); - new Fl_Sys_Menu_Bar(0,0,0,0); - } - if (!window_menu_items_count) { - window_menu_items_count = 6; - window_menu_items = (Fl_Menu_Item*)calloc(window_menu_items_count, sizeof(Fl_Menu_Item)); - } - rank = fl_sys_menu_bar->Fl_Menu_::insert(rank, "Window", 0, NULL, window_menu_items, FL_SUBMENU_POINTER); - localized_Window = NSLocalizedString(@"Window", nil); - window_menu_items[0].label("Minimize"); - window_menu_items[0].callback(minimize_win_cb); - window_menu_items[0].shortcut(FL_COMMAND+'m'); - window_menu_items[0].flags = FL_MENU_DIVIDER; - first_window_menu_item = 1; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200 && window_menu_style() != Fl_Sys_Menu_Bar::tabbing_mode_none) { - window_menu_items[1].label("Show Previous Tab"); - window_menu_items[1].callback(previous_tab_cb); - window_menu_items[1].shortcut(FL_SHIFT+FL_CTRL+0x9); - window_menu_items[2].label("Show Next Tab"); - window_menu_items[2].callback(next_tab_cb); - window_menu_items[2].shortcut(FL_CTRL+0x9); - window_menu_items[3].label("Move Tab To New Window"); - window_menu_items[3].callback(move_tab_cb); - window_menu_items[4].label("Merge All Windows"); - window_menu_items[4].callback(merge_all_windows_cb); - window_menu_items[4].flags = FL_MENU_DIVIDER; - first_window_menu_item = 5; - } -#endif - fl_sys_menu_bar->menu_end(); - fl_sys_menu_bar->update(); -} - - -void Fl_MacOS_Sys_Menu_Bar_Driver::new_window(Fl_Window *win) -{ - if (!window_menu_style() || !win->label()) return; - int index = window_menu_items->size() - 1; - if (index >= window_menu_items_count - 1) { - window_menu_items_count += 5; - window_menu_items = (Fl_Menu_Item*)realloc(window_menu_items, - window_menu_items_count * sizeof(Fl_Menu_Item)); - Fl_Menu_Item *item = (Fl_Menu_Item*)fl_sys_menu_bar->find_item("Window"); - item->user_data(window_menu_items); - } - const char *p = win->iconlabel() ? win->iconlabel() : win->label(); - window_menu_items[index].label(p); - window_menu_items[index].callback(window_menu_cb); - window_menu_items[index].user_data(win); - window_menu_items[index].flags = FL_MENU_RADIO; - window_menu_items[index+1].label(NULL); - window_menu_items[index].setonly(); - fl_sys_menu_bar->update(); -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::remove_window(Fl_Window *win) -{ - if (!window_menu_style()) return; - int index = first_window_menu_item; - if (index < 1) return; - while (true) { - Fl_Menu_Item *item = window_menu_items + index; - if (!item->label()) return; - if (item->user_data() == win) { - bool doit = item->value(); - int count = window_menu_items->size(); - if (count - index - 1 > 0) memmove(item, item + 1, (count - index - 1)*sizeof(Fl_Menu_Item)); - memset(window_menu_items + count - 2, 0, sizeof(Fl_Menu_Item)); - if (doit) { // select Fl::first_window() in Window menu - item = window_menu_items + first_window_menu_item; - while (item->label() && item->user_data() != Fl::first_window()) item++; - if (item->label()) { - ((Fl_Window*)item->user_data())->show(); - item->setonly(); - } - } - bar->update(); - break; - } - index++; - } -} - -void Fl_MacOS_Sys_Menu_Bar_Driver::rename_window(Fl_Window *win) -{ - if (!window_menu_style()) return; - int index = first_window_menu_item; - if (index < 1) return; - while (true) { - Fl_Menu_Item *item = window_menu_items + index; - if (!item->label()) return; - if (item->user_data() == win) { - item->label(win->iconlabel() ? win->iconlabel() : win->label()); - bar->update(); - return; - } - index++; - } -} - -void fl_mac_set_about(Fl_Callback *cb, void *user_data, int shortcut) { - Fl_Sys_Menu_Bar::about(cb, user_data); -} - - -void Fl_MacOS_Sys_Menu_Bar_Driver::play_menu(const Fl_Menu_Item *item) { - // Use the accessibility interface to programmatically open a menu of the system menubar - CFArrayRef children = NULL; - CFIndex count = 0; - AXUIElementRef element; - char *label = remove_ampersand(item->label()); - NSString *mac_name = NSLocalizedString([NSString stringWithUTF8String:label], nil); - free(label); - AXUIElementRef appElement = AXUIElementCreateApplication(getpid()); - AXUIElementRef menu_bar = NULL; - AXError error = AXUIElementCopyAttributeValue(appElement, kAXMenuBarAttribute, - (CFTypeRef *)&menu_bar); - if (!error) error = AXUIElementGetAttributeValueCount(menu_bar, kAXChildrenAttribute, &count); - if (!error) error = AXUIElementCopyAttributeValues(menu_bar, kAXChildrenAttribute, 0, count, - &children); - if (!error) { - NSEnumerator *enumerator = [(NSArray*)children objectEnumerator]; - [enumerator nextObject]; // skip Apple menu - [enumerator nextObject]; // skip application menu - bool need_more = true; - while (need_more && (element = (AXUIElementRef)[enumerator nextObject]) != nil) { - CFTypeRef title = NULL; - need_more = ( AXUIElementCopyAttributeValue(element, kAXTitleAttribute, &title) == 0 ); - if (need_more && [(NSString*)title isEqualToString:mac_name]) { - AXUIElementPerformAction(element, kAXPressAction); - need_more = false; - } - if (title) CFRelease(title); - } - } - if (menu_bar) CFRelease(menu_bar); - if (children) CFRelease(children); - CFRelease(appElement); -} - -#endif /* __APPLE__ */ diff --git a/src/Fl_Native_File_Chooser_MAC.mm b/src/Fl_Native_File_Chooser_MAC.mm deleted file mode 100644 index b3dd9ae8f..000000000 --- a/src/Fl_Native_File_Chooser_MAC.mm +++ /dev/null @@ -1,825 +0,0 @@ -// -// FLTK native OS file chooser widget for macOS -// -// Copyright 2004 Greg Ercolano. -// Copyright 1998-2024 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// TODO: -// o When doing 'open file', only dir is preset, not filename. -// Possibly 'preset_file' could be used to select the filename. -// - -#include -#include // for fl_mac_os_version -#include -#include -#include -#include -#define MAXFILTERS 80 -#import -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 -# import -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 -const NSInteger NSModalResponseOK = NSFileHandlingPanelOKButton; -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -const NSUInteger NSControlSizeRegular = NSRegularControlSize; -#endif - -class Fl_Quartz_Native_File_Chooser_Driver : public Fl_Native_File_Chooser_Driver { -private: - int _btype; // kind-of browser to show() - int _options; // general options - NSSavePanel *_panel; - char **_pathnames; // array of pathnames - int _tpathnames; // total pathnames - char *_directory; // default pathname to use - char *_title; // title for window - char *_preset_file; // the 'save as' filename - - char *_filter; // user-side search filter, eg: - // C Files\t*.[ch]\nText Files\t*.txt" - - char *_filt_names; // filter names (tab delimited) - // eg. "C Files\tText Files" - - char *_filt_patt[MAXFILTERS]; - // array of filter patterns, eg: - // _filt_patt[0]="*.{cxx,h}" - // _filt_patt[1]="*.txt" - - int _filt_total; // parse_filter() # of filters loaded - int _filt_value; // index of the selected filter - char *_errmsg; // error message - - // Private methods - void errmsg(const char *msg); - void clear_pathnames(); - void set_single_pathname(const char *s); - int get_saveas_basename(void); - void clear_filters(); - void parse_filter(const char *from); - int post(); - int runmodal(); -public: - Fl_Quartz_Native_File_Chooser_Driver(int val); - ~Fl_Quartz_Native_File_Chooser_Driver(); - void type(int t) FL_OVERRIDE; - int type() const FL_OVERRIDE; - void options(int o) FL_OVERRIDE; - int options() const FL_OVERRIDE; - int count() const FL_OVERRIDE; - const char *filename() const FL_OVERRIDE; - const char *filename(int i) const FL_OVERRIDE; - void directory(const char *val) FL_OVERRIDE; - const char *directory() const FL_OVERRIDE; - void title(const char *t) FL_OVERRIDE; - const char* title() const FL_OVERRIDE; - const char *filter() const FL_OVERRIDE; - void filter(const char *f) FL_OVERRIDE; - int filters() const FL_OVERRIDE; - void filter_value(int i) FL_OVERRIDE; - int filter_value() const FL_OVERRIDE; - void preset_file(const char*f) FL_OVERRIDE; - const char* preset_file() const FL_OVERRIDE; - const char *errmsg() const FL_OVERRIDE; - int show() FL_OVERRIDE; -}; - -Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) { - platform_fnfc = new Fl_Quartz_Native_File_Chooser_Driver(val); -} - -// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS -void Fl_Quartz_Native_File_Chooser_Driver::clear_pathnames() { - if ( _pathnames ) { - while ( --_tpathnames >= 0 ) { - _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); - } - delete [] _pathnames; - _pathnames = NULL; - } - _tpathnames = 0; -} - -// SET A SINGLE PATHNAME -void Fl_Quartz_Native_File_Chooser_Driver::set_single_pathname(const char *s) { - clear_pathnames(); - _pathnames = new char*[1]; - _pathnames[0] = strnew(s); - _tpathnames = 1; -} - -// CONSTRUCTOR -Fl_Quartz_Native_File_Chooser_Driver::Fl_Quartz_Native_File_Chooser_Driver(int val) : - Fl_Native_File_Chooser_Driver(val) { - _btype = val; - _panel = NULL; - _options = Fl_Native_File_Chooser::NO_OPTIONS; - _pathnames = NULL; - _tpathnames = 0; - _title = NULL; - _filter = NULL; - _filt_names = NULL; - memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS); - _filt_total = 0; - _filt_value = 0; - _directory = NULL; - _preset_file = NULL; - _errmsg = NULL; -} - -// DESTRUCTOR -Fl_Quartz_Native_File_Chooser_Driver::~Fl_Quartz_Native_File_Chooser_Driver() { - // _opts // nothing to manage - // _options // nothing to manage - // _keepstate // nothing to manage - // _tempitem // nothing to manage - clear_pathnames(); - _directory = strfree(_directory); - _title = strfree(_title); - _preset_file = strfree(_preset_file); - _filter = strfree(_filter); - //_filt_names // managed by clear_filters() - //_filt_patt[i] // managed by clear_filters() - //_filt_total // managed by clear_filters() - clear_filters(); - //_filt_value // nothing to manage - _errmsg = strfree(_errmsg); -} - -// GET TYPE OF BROWSER -int Fl_Quartz_Native_File_Chooser_Driver::type() const { - return(_btype); -} - -// SET OPTIONS -void Fl_Quartz_Native_File_Chooser_Driver::options(int val) { - _options = val; -} - -// GET OPTIONS -int Fl_Quartz_Native_File_Chooser_Driver::options() const { - return(_options); -} - -// SHOW THE BROWSER WINDOW -// Returns: -// 0 - user picked a file -// 1 - user cancelled -// -1 - failed; errmsg() has reason -// -int Fl_Quartz_Native_File_Chooser_Driver::show() { - - // Make sure fltk interface updates before posting our dialog - Fl::flush(); - - // POST BROWSER - int err = post(); - - return(err); -} - -// SET ERROR MESSAGE -// Internal use only. -// -void Fl_Quartz_Native_File_Chooser_Driver::errmsg(const char *msg) { - _errmsg = strfree(_errmsg); - _errmsg = strnew(msg); -} - -// RETURN ERROR MESSAGE -const char *Fl_Quartz_Native_File_Chooser_Driver::errmsg() const { - return(_errmsg ? _errmsg : "No error"); -} - -// GET FILENAME -const char* Fl_Quartz_Native_File_Chooser_Driver::filename() const { - if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); - return(""); -} - -// GET FILENAME FROM LIST OF FILENAMES -const char* Fl_Quartz_Native_File_Chooser_Driver::filename(int i) const { - if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); - return(""); -} - -// GET TOTAL FILENAMES CHOSEN -int Fl_Quartz_Native_File_Chooser_Driver::count() const { - return(_tpathnames); -} - -// PRESET PATHNAME -// Value can be NULL for none. -// -void Fl_Quartz_Native_File_Chooser_Driver::directory(const char *val) { - _directory = strfree(_directory); - _directory = strnew(val); -} - -// GET PRESET PATHNAME -// Returned value can be NULL if none set. -// -const char* Fl_Quartz_Native_File_Chooser_Driver::directory() const { - return(_directory); -} - -// SET TITLE -// Value can be NULL if no title desired. -// -void Fl_Quartz_Native_File_Chooser_Driver::title(const char *val) { - _title = strfree(_title); - _title = strnew(val); -} - -// GET TITLE -// Returned value can be NULL if none set. -// -const char *Fl_Quartz_Native_File_Chooser_Driver::title() const { - return(_title); -} - -// SET FILTER -// Can be NULL if no filter needed -// -void Fl_Quartz_Native_File_Chooser_Driver::filter(const char *val) { - _filter = strfree(_filter); - _filter = strnew(val); - - // Parse filter user specified - // IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt" - // OUT: _filt_names = "C Files\tText Files" - // _filt_patt[0] = "*.{cxx,h}" - // _filt_patt[1] = "*.txt" - // _filt_total = 2 - // - parse_filter(_filter); -} - -// GET FILTER -// Returned value can be NULL if none set. -// -const char *Fl_Quartz_Native_File_Chooser_Driver::filter() const { - return(_filter); -} - -// CLEAR ALL FILTERS -// Internal use only. -// -void Fl_Quartz_Native_File_Chooser_Driver::clear_filters() { - _filt_names = strfree(_filt_names); - for (int i=0; i<_filt_total; i++) { - _filt_patt[i] = strfree(_filt_patt[i]); - } - _filt_total = 0; -} - -// PARSE USER'S FILTER SPEC -// Parses user specified filter ('in'), -// breaks out into _filt_patt[], _filt_names, and _filt_total. -// -// Handles: -// IN: OUT:_filt_names OUT: _filt_patt -// ------------------------------------ ------------------ --------------- -// "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}" -// "*.[abc]" "*.[abc] Files" "*.[abc]" -// "*.txt" "*.txt Files" "*.c" -// "C Files\t*.[ch]" "C Files" "*.[ch]" -// "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]" -// -// Parsing Mode: -// IN:"C Files\t*.{cxx,h}" -// ||||||| ||||||||| -// mode: nnnnnnn wwwwwwwww -// \_____/ \_______/ -// Name Wildcard -// -void Fl_Quartz_Native_File_Chooser_Driver::parse_filter(const char *in) { - clear_filters(); - if ( ! in ) return; - int has_name = strchr(in, '\t') ? 1 : 0; - - char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard - char wildcard[1024] = ""; // parsed wildcard - char name[1024] = ""; - - // Parse filter user specified - for ( ; 1; in++ ) { - - //// DEBUG - //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n", - //// *in, mode, name, wildcard); - - switch (*in) { - // FINISHED PARSING NAME? - case '\t': - if ( mode != 'n' ) goto regchar; - mode = 'w'; - break; - - // ESCAPE NEXT CHAR - case '\\': - ++in; - goto regchar; - - // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? - case '\r': - case '\n': - case '\0': - // TITLE - // If user didn't specify a name, make one - // - if ( name[0] == '\0' ) { - snprintf(name, sizeof(name), "%.*s Files", (int)sizeof(name)-10, wildcard); - } - // APPEND NEW FILTER TO LIST - if ( wildcard[0] ) { - // Add to filtername list - // Tab delimit if more than one. We later break - // tab delimited string into CFArray with - // CFStringCreateArrayBySeparatingStrings() - // - if ( _filt_total ) { - _filt_names = strapp(_filt_names, "\t"); - } - _filt_names = strapp(_filt_names, name); - - // Add filter to the pattern array - _filt_patt[_filt_total++] = strnew(wildcard); - } - // RESET - wildcard[0] = name[0] = '\0'; - mode = strchr(in, '\t') ? 'n' : 'w'; - // DONE? - if ( *in == '\0' ) return; // done - else continue; // not done yet, more filters - - // Parse all other chars - default: // handle all non-special chars - regchar: // handle regular char - switch ( mode ) { - case 'n': chrcat(name, *in); continue; - case 'w': chrcat(wildcard, *in); continue; - } - break; - } - } - //NOTREACHED -} - -// SET PRESET FILE -// Value can be NULL for none. -// -void Fl_Quartz_Native_File_Chooser_Driver::preset_file(const char* val) { - _preset_file = strfree(_preset_file); - _preset_file = strnew(val); -} - -// PRESET FILE -// Returned value can be NULL if none set. -// -const char* Fl_Quartz_Native_File_Chooser_Driver::preset_file() const { - return(_preset_file); -} - -void Fl_Quartz_Native_File_Chooser_Driver::filter_value(int val) { - _filt_value = val; -} - -int Fl_Quartz_Native_File_Chooser_Driver::filter_value() const { - return(_filt_value); -} - -int Fl_Quartz_Native_File_Chooser_Driver::filters() const { - return(_filt_total); -} - -#define UNLIKELYPREFIX "___fl_very_unlikely_prefix_" - -int Fl_Quartz_Native_File_Chooser_Driver::get_saveas_basename(void) { - char *q = fl_strdup( [[[_panel URL] path] UTF8String] ); - if ( !(_options & Fl_Native_File_Chooser::SAVEAS_CONFIRM) ) { - const char *d = [[[[_panel URL] path] stringByDeletingLastPathComponent] UTF8String]; - int l = (int)strlen(d) + 1; - if (strcmp(d, "/") == 0) l = 1; - int lu = (int)strlen(UNLIKELYPREFIX); - int ln = (int)strlen(q+l); - if (ln >= lu) { - // Remove UNLIKELYPREFIX between directory and filename parts - if (memcmp(q+l, UNLIKELYPREFIX, lu) == 0) memmove(q + l, q + l + lu, strlen(q + l + lu) + 1); - } - } - set_single_pathname( q ); - free(q); - return 0; -} - -// SET THE TYPE OF BROWSER -void Fl_Quartz_Native_File_Chooser_Driver::type(int val) { - _btype = val; -} - -/* Input - filter= "C files\t*.{c,h}\nText files\t*.txt\n" - patterns[0] = "*.{c,h}" - patterns[1] = "*.txt" - count = 2 - Return: - "C files (*.{c,h})\nText files (*.txt)\n" - */ -static char *prepareMacFilter(int count, const char *filter, char **patterns) { - int rank = 0, l = 0; - for (int i = 0; i < count; i++) { - l += strlen(patterns[i]) + 3; - } - const char *p = filter; - const int t_size = (int)strlen(p) + l + 1; - char *q; q = new char[t_size]; - const char *r, *s; - char *t; - t = q; - do { // copy to t what is in filter removing what is between \t and \n, if any - r = strchr(p, '\n'); - if (!r) r = p + strlen(p); - s = strchr(p, '\t'); - if (s && s < r) { - memcpy(q, p, s - p); - q += s - p; - if (rank < count) { - snprintf(q, t_size-(q-t), " (%s)", patterns[rank]); q += strlen(q); - } - } - else { - memcpy(q, p, r - p); - q += r - p; - } - rank++; - *(q++) = '\n'; - if (*r) p = r + 1; else p = r; - } while(*p); - *q = 0; - return t; -} - -@interface FLopenDelegate : NSObject -{ - NSPopUpButton *nspopup; - char **filter_pattern; -} -- (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern; -- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; -- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url; -@end -@implementation FLopenDelegate -- (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern -{ - nspopup = popup; - filter_pattern = pattern; - return self; -} -- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename -{ - if ( [nspopup indexOfSelectedItem] == [nspopup numberOfItems] - 1) return YES; - BOOL isdir = NO; - [[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isdir]; - if (isdir) return YES; - if ( fl_filename_match([filename fileSystemRepresentation], filter_pattern[([nspopup indexOfSelectedItem])]) ) return YES; - return NO; -} -- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url -{ - return [self panel:sender shouldShowFilename:[url path]]; -} -@end - -@interface FLsaveDelegate : NSObject -{ - NSSavePanel *dialog; - BOOL saveas_confirm; -} -- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag; -- (void)control_allowed_types:(const char *)p; -- (void)changedPopup:(id)sender; -- (void)panel:(NSSavePanel*)p; -- (void)option:(BOOL)o; -@end -@implementation FLsaveDelegate -- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag -{ - if ( !okFlag || saveas_confirm ) return filename; - // User has clicked save, and no overwrite confirmation should occur. - // To get the latter, we need to change the name we return (hence the prefix): - return [@ UNLIKELYPREFIX stringByAppendingString:filename]; -} -- (void)control_allowed_types:(const char *)p -{ - NSString *ext = [NSString stringWithUTF8String:p]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0 - if (@available(macOS 11.0, *)) { - UTType *type = [UTType typeWithFilenameExtension:ext]; // 11.0 + framework UniformTypeIdentifiers - [dialog setAllowedContentTypes:[NSArray arrayWithObject:type]]; // 11.0 - } - else -#endif - if (fl_mac_os_version >= 100900) { - [dialog performSelector:@selector(setAllowedFileTypes:) - withObject:[NSArray arrayWithObject:ext]]; - } -} -- (void)changedPopup:(id)sender -// runs when the save panel popup menu changes output file type -// correspondingly changes the extension of the output file name -{ - char *s = fl_strdup([[(NSPopUpButton*)sender titleOfSelectedItem] UTF8String]); - if (!s) return; - char *p = strchr(s, '('); - if (!p) p = s; - p = strchr(p, '.'); - if (!p) {free(s); return;} - p++; - while (*p == ' ') p++; - if (!p || *p == '{') {free(s); return;} - char *q = p+1; - while (*q != ' ' && *q != ')' && *q != 0) q++; - *q = 0; - NSString *ns = [NSString stringWithFormat:@"%@.%@", - [[dialog performSelector:@selector(nameFieldStringValue)] stringByDeletingPathExtension], - [NSString stringWithUTF8String:p]]; - [self control_allowed_types:p]; - free(s); - [dialog performSelector:@selector(setNameFieldStringValue:) withObject:ns]; -} -- (void)panel:(NSSavePanel*)p -{ - dialog = p; -} -- (void) option:(BOOL)o -{ - saveas_confirm = o; -} -@end - - -@interface FLHiddenFilesAction : NSObject -{ -@public - NSSavePanel *panel; - NSButton *button; -} -- (void)action; -@end -@implementation FLHiddenFilesAction -- (void)action { - [panel setShowsHiddenFiles:[button intValue]]; // 10.6 -} -@end - - -static NSPopUpButton *createPopupAccessory(NSSavePanel *panel, const char *filter, const char *title, int rank) -{ - NSPopUpButton *popup = nil; - NSRect rectview = NSMakeRect(5, 5, 350, filter ? 60 : 30); - NSView *view = [[[NSView alloc] initWithFrame:rectview] autorelease]; - NSRect rectbox = NSMakeRect(0, 3, 140, 20 ); - // the "Show hidden files" button - NSRect hidden_files_rect = {{150, 0}, {80, 30}}; - if (filter) hidden_files_rect.origin.y = 35; - NSButton *hidden_files = [[[NSButton alloc] initWithFrame:hidden_files_rect] autorelease]; - [hidden_files setButtonType: -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - NSButtonTypeSwitch -#else - NSSwitchButton -#endif - ]; - [hidden_files setTitle:[NSString stringWithUTF8String:Fl_File_Chooser::hidden_label]]; - [hidden_files sizeToFit]; - [hidden_files setIntValue:0]; - [view addSubview:hidden_files]; - static FLHiddenFilesAction *target = [[FLHiddenFilesAction alloc] init]; // never released - target->panel = panel; - target->button = hidden_files; - [hidden_files setAction:@selector(action)]; - [hidden_files setTarget:target]; - if (filter) { - NSBox *box = [[[NSBox alloc] initWithFrame:rectbox] autorelease]; - NSRect rectpop = NSMakeRect(105, 0, 246, 30 ); - popup = [[[NSPopUpButton alloc ] initWithFrame:rectpop pullsDown:NO] autorelease]; - [view addSubview:box]; - [view addSubview:popup]; - NSString *nstitle = [[NSString alloc] initWithUTF8String:title]; - [box setTitle:nstitle]; - [box setTitlePosition:NSBelowTop]; - [nstitle release]; - NSFont *font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; - [box setTitleFont:font]; - [box sizeToFit]; - // horizontally move box to fit the locale-dependent width of its title - NSRect r=[box frame]; - r.origin.x = rectpop.origin.x - r.size.width; - r.origin.y = rectpop.origin.y + (rectpop.size.height - r.size.height) / 2; - [box setFrame:r]; - CFStringRef tab = CFSTR("\n"); - CFStringRef tmp_cfs; - tmp_cfs = CFStringCreateWithCString(NULL, filter, kCFStringEncodingUTF8); - CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, tmp_cfs, tab); - CFRelease(tmp_cfs); - CFRelease(tab); - [popup addItemsWithTitles:(NSArray*)array]; - NSMenuItem *item = [popup itemWithTitle:@""]; - if (item) [popup removeItemWithTitle:@""]; - CFRelease(array); - [popup selectItemAtIndex:rank]; - } - [panel setAccessoryView:view]; - return popup; -} - -int Fl_Quartz_Native_File_Chooser_Driver::runmodal() -{ - NSString *dir = nil; - NSString *fname = nil; - NSString *preset = nil; - NSInteger retval; - if (_preset_file) { - preset = [[NSString alloc] initWithUTF8String:_preset_file]; - if (strchr(_preset_file, '/') != NULL) { - dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]]; - } - fname = [preset lastPathComponent]; - } - if (_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory]; -#if defined(__BLOCKS__) - { - bool usepath = false; - NSString *path = nil; - if (dir && fname && [_panel isKindOfClass:[NSOpenPanel class]]) { - // STR #3406: If both dir + fname specified, combine and pass to setDirectoryURL - path = [[NSString alloc] initWithFormat:@"%@/%@", dir, fname]; // dir+fname -> path - // See if full path to file exists - // If dir exists but fname doesn't, avoid using setDirectoryURL, - // otherwise NSSavePanel falls back to showing user's Documents dir. - // - if ( [[NSFileManager defaultManager] fileExistsAtPath:path] ) usepath = true; - } - if (usepath) { - // Set only if full path exists - [_panel setDirectoryURL:[NSURL fileURLWithPath:path]]; // 10.6 - } else { // didn't setDirectoryURL to full path? Set dir + fname separately.. - if (dir) [_panel setDirectoryURL:[NSURL fileURLWithPath:dir]]; // 10.6 - if (fname) [_panel setNameFieldStringValue:fname]; // 10.6 - } - [path release]; - if ([NSApp mainWindow]) { - __block NSInteger complete = -1; - [_panel beginSheetModalForWindow:[NSApp mainWindow] completionHandler:^(NSInteger returnCode) { - complete = returnCode; // this block runs after OK or Cancel was triggered in file dialog - }]; // 10.6 this message returns immediately and begins the file dialog as a sheet - while (complete == -1) Fl::wait(100); // loop until end of file dialog - retval = complete; - } else { - retval = [_panel runModal]; - } - } -#else // !__BLOCKS__ - // the deprecation warning runs only without blocks -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - retval = [_panel runModalForDirectory:dir file:fname]; // deprecated in 10.6 -#pragma clang diagnostic pop -#endif // __BLOCKS__ - [dir release]; - [preset release]; - return (retval == NSModalResponseOK ? 1 : 0); -} - -// POST BROWSER -// Internal use only. -// Assumes '_opts' has been initialized. -// -// Returns: -// 0 - user picked a file -// 1 - user cancelled -// -1 - failed; errmsg() has reason -// -int Fl_Quartz_Native_File_Chooser_Driver::post() { - // INITIALIZE BROWSER - if ( _filt_total == 0 ) { // Make sure they match - _filt_value = 0; // TBD: move to someplace more logical? - } - fl_open_display(); - NSAutoreleasePool *localPool; - localPool = [[NSAutoreleasePool alloc] init]; - switch (_btype) { - case Fl_Native_File_Chooser::BROWSE_FILE: - case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: - case Fl_Native_File_Chooser::BROWSE_DIRECTORY: - case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: - _panel = [NSOpenPanel openPanel]; - break; - case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: - case Fl_Native_File_Chooser::BROWSE_SAVE_FILE: - _panel = [NSSavePanel savePanel]; - break; - } - BOOL is_open_panel = [_panel isKindOfClass:[NSOpenPanel class]]; - if (_title) { - SEL title_or_message = (is_open_panel && fl_mac_os_version >= 101200) ? - @selector(setMessage:) : @selector(setTitle:); - [_panel performSelector:title_or_message withObject:[NSString stringWithUTF8String:_title]]; - } - switch (_btype) { - case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: - [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES]; - break; - case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: - [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES]; - /* FALLTHROUGH */ - case Fl_Native_File_Chooser::BROWSE_DIRECTORY: - [(NSOpenPanel*)_panel setCanChooseDirectories:YES]; - break; - case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: - [_panel setCanCreateDirectories:YES]; - break; - } - - // SHOW THE DIALOG - NSWindow *key = [NSApp keyWindow]; - NSPopUpButton *popup = nil; - if ( is_open_panel ) { - if (_filt_total) { - char *t = prepareMacFilter(_filt_total, _filter, _filt_patt); - popup = createPopupAccessory(_panel, t, Fl_File_Chooser::show_label, 0); - delete[] t; - [[popup menu] addItem:[NSMenuItem separatorItem]]; - [popup addItemWithTitle:[NSString stringWithUTF8String:Fl_File_Chooser::all_files_label]]; - [popup setAction:@selector(validateVisibleColumns)]; - [popup setTarget:(NSObject*)_panel]; - FLopenDelegate *openDelegate = [[[FLopenDelegate alloc] init] autorelease]; - [openDelegate setPopup:popup filter_pattern:_filt_patt]; - [_panel setDelegate:openDelegate]; - } else createPopupAccessory(_panel, NULL, Fl_File_Chooser::show_label, 0); - } - else { - FLsaveDelegate *saveDelegate = [[[FLsaveDelegate alloc] init] autorelease]; - [_panel setAllowsOtherFileTypes:YES]; - [_panel setDelegate:saveDelegate]; - [saveDelegate option:(_options & Fl_Native_File_Chooser::SAVEAS_CONFIRM)]; - if (_filt_total) { - if (_filt_value >= _filt_total) _filt_value = _filt_total - 1; - char *t = prepareMacFilter(_filt_total, _filter, _filt_patt); - popup = createPopupAccessory(_panel, t, [[_panel nameFieldLabel] UTF8String], _filt_value); - delete[] t; - if (_options & Fl_Native_File_Chooser::USE_FILTER_EXT) { - [popup setAction:@selector(changedPopup:)]; - [popup setTarget:saveDelegate]; - [saveDelegate panel:(NSSavePanel*)_panel]; - if (fl_mac_os_version >= 100900) { - char *p = _filt_patt[_filt_value]; - char *q = strchr(p, '.'); if(!q) q = p-1; - do q++; while (*q==' ' || *q=='{'); - p = fl_strdup(q); - q = strchr(p, ','); if (q) *q = 0; - [saveDelegate control_allowed_types:p]; - free(p); - } - } - [_panel setCanSelectHiddenExtension:YES]; - [_panel setExtensionHidden:NO]; - } else createPopupAccessory(_panel, NULL, Fl_File_Chooser::show_label, 0); - } - int retval = runmodal(); - if (_filt_total) { - _filt_value = (int)[popup indexOfSelectedItem]; - } - if ( retval == 1 ) { - if (is_open_panel) { - clear_pathnames(); - NSArray *array = [(NSOpenPanel*)_panel URLs]; - _tpathnames = (int)[array count]; - _pathnames = new char*[_tpathnames]; - for(int i = 0; i < _tpathnames; i++) { - _pathnames[i] = strnew([[(NSURL*)[array objectAtIndex:i] path] UTF8String]); - } - } - else get_saveas_basename(); - } - [key makeKeyWindow]; - [localPool release]; - return (retval == 1 ? 0 : 1); -} diff --git a/src/Fl_Native_File_Chooser_WIN32.cxx b/src/Fl_Native_File_Chooser_WIN32.cxx deleted file mode 100644 index f33af11c8..000000000 --- a/src/Fl_Native_File_Chooser_WIN32.cxx +++ /dev/null @@ -1,1061 +0,0 @@ -// -// FLTK native OS file chooser widget -// -// Copyright 1998-2021 by Bill Spitzak and others. -// Copyright 2004 Greg Ercolano. -// API changes + filter improvements by Nathan Vander Wilt 2005 -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Any application to multi-folder implementation: -// http://www.codeproject.com/dialog/selectfolder.asp -// - -#ifndef FL_DOXYGEN // PREVENT DOXYGEN'S USE OF THIS FILE -#include - -#include // malloc -#include // snprintf -#include - -#define FNFC_MAX_PATH 32768 // XXX: MAX_PATH under win32 is 260, too small for modern use - -#include // fl_strdup() -#include "flstring.h" // fl_strlcpy()/cat() -#include -# include -# include // OPENFILENAMEW, GetOpenFileName() -# include // BROWSEINFOW, SHBrowseForFolder() -# include // FL_EXPORT -#include // fl_open_display - - -class Fl_WinAPI_Native_File_Chooser_Driver : public Fl_Native_File_Chooser_Driver { -private: - int _btype; // kind-of browser to show() - int _options; // general options - OPENFILENAMEW *_ofn_ptr; // GetOpenFileName() & GetSaveFileName() struct - BROWSEINFOW *_binf_ptr; // SHBrowseForFolder() struct - WCHAR *_wpattern; // pattern buffer for filter - char **_pathnames; // array of pathnames - int _tpathnames; // total pathnames - char *_directory; // default pathname to use - char *_title; // title for window - char *_filter; // user-side search filter - char *_parsedfilt; // filter parsed for Windows dialog - int _nfilters; // number of filters parse_filter counted - char *_preset_file; // the file to preselect - char *_errmsg; // error message - - // Private methods - void errmsg(const char *msg); - - void clear_pathnames(); - void set_single_pathname(const char *s); - void add_pathname(const char *s); - - void ClearOFN(); - void ClearBINF(); - void Win2Unix(char *s); - void Unix2Win(char *s); - bool IsUnixPath(const char *s); - int showfile(); - int showdir(); - - void parse_filter(const char *); - void clear_filters(); - void add_filter(const char *, const char *); -public: - Fl_WinAPI_Native_File_Chooser_Driver(int val); - ~Fl_WinAPI_Native_File_Chooser_Driver(); - void type(int t) FL_OVERRIDE; - int type() const FL_OVERRIDE; - void options(int o) FL_OVERRIDE; - int options() const FL_OVERRIDE; - int count() const FL_OVERRIDE; - const char *filename() const FL_OVERRIDE; - const char *filename(int i) const FL_OVERRIDE; - void directory(const char *val) FL_OVERRIDE; - const char *directory() const FL_OVERRIDE; - void title(const char *t) FL_OVERRIDE; - const char* title() const FL_OVERRIDE; - const char *filter() const FL_OVERRIDE; - void filter(const char *f) FL_OVERRIDE; - int filters() const FL_OVERRIDE; - void filter_value(int i) FL_OVERRIDE; - int filter_value() const FL_OVERRIDE; - void preset_file(const char*f) FL_OVERRIDE; - const char* preset_file() const FL_OVERRIDE; - const char *errmsg() const FL_OVERRIDE; - int show() FL_OVERRIDE; -}; - - -Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) { - platform_fnfc = new Fl_WinAPI_Native_File_Chooser_Driver(val); -} - - -static LPCWSTR utf8towchar(const char *in); -static char *wchartoutf8(LPCWSTR in); - - -#define LCURLY_CHR '{' -#define RCURLY_CHR '}' -#define LBRACKET_CHR '[' -#define RBRACKET_CHR ']' - -// STATIC: Print Windows 'double-null' string (debug) -#ifdef DEBUG -#include -static void dnullprint(char *wp) { - if ( ! wp ) return; - for ( int t=0; true; t++ ) { - if ( wp[t] == '\0' && wp[t+1] == '\0' ) { - printf("\\0\\0"); - fflush(stdout); - return; - } else if ( wp[t] == '\0' ) { - printf("\\0"); - } else { - printf("%c",wp[t]); - } - } -} -#endif - -// Return length of double-null string -// Includes single nulls in count, excludes trailing double-null. -// -// 1234 567 -// |||/\||| -// IN: "one\0two\0\0" -// OUT: 7 -// -static int dnulllen(const char *wp) { - int len = 0; - while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) { - ++wp; - ++len; - } - return(len); -} - -// STATIC: Append a string to another, leaving terminated with DOUBLE NULL. -// Automatically handles extending length of string. -// wp can be NULL (a new wp will be allocated and initialized). -// string must be NULL terminated. -// The pointer wp may be modified on return. -// -static void dnullcat(char*&wp, const char *string, int n = -1 ) { - //DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n"); - size_t inlen = ( n < 0 ) ? strlen(string) : n; - char *wp2 = 0; // used to point at end of last string - if ( ! wp ) { - wp = new char[inlen + 4]; - *(wp+0) = '\0'; - *(wp+1) = '\0'; - wp2 = wp; // no "last string", point at begin of buffer - } else { - int wplen = dnulllen(wp); - // Make copy of wp into larger buffer - char *tmp = new char[wplen + inlen + 4]; - memcpy(tmp, wp, wplen+2); // copy of wp plus doublenull - delete[] wp; // delete old wp - wp = tmp; // use new copy - wp2 = wp + wplen + 1; // point at second null - //DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen); - } - - // *wp2 points at second null; the buffer is large enough to copy the string! - strcpy(wp2, string); - *(wp2+inlen+1) = '\0'; // Leave string double-null terminated - //DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n"); -} - -// CTOR -Fl_WinAPI_Native_File_Chooser_Driver::Fl_WinAPI_Native_File_Chooser_Driver(int val) : - Fl_Native_File_Chooser_Driver(val) { - _btype = val; - _options = Fl_Native_File_Chooser::NO_OPTIONS; - _ofn_ptr = new OPENFILENAMEW; - _binf_ptr = new BROWSEINFOW; - _wpattern = 0; - memset((void*)_ofn_ptr, 0, sizeof(OPENFILENAMEW)); - _ofn_ptr->lStructSize = sizeof(OPENFILENAMEW); - _ofn_ptr->hwndOwner = 0L; - memset((void*)_binf_ptr, 0, sizeof(BROWSEINFOW)); - _pathnames = NULL; - _tpathnames = 0; - _directory = NULL; - _title = NULL; - _filter = NULL; - _parsedfilt = NULL; - _nfilters = 0; - _preset_file = NULL; - _errmsg = NULL; -} - -// DTOR -Fl_WinAPI_Native_File_Chooser_Driver::~Fl_WinAPI_Native_File_Chooser_Driver() { - //_pathnames // managed by clear_pathnames() - //_tpathnames // managed by clear_pathnames() - _directory = strfree(_directory); - _title = strfree(_title); - _filter = strfree(_filter); - //_parsedfilt // managed by clear_filters() - //_nfilters // managed by clear_filters() - _preset_file = strfree(_preset_file); - _errmsg = strfree(_errmsg); - clear_filters(); - clear_pathnames(); - ClearOFN(); - ClearBINF(); - delete _binf_ptr; - delete _ofn_ptr; - if ( _wpattern ) delete[] _wpattern; -} - -// SET TYPE OF BROWSER -void Fl_WinAPI_Native_File_Chooser_Driver::type(int val) { - _btype = val; -} - -// GET TYPE OF BROWSER -int Fl_WinAPI_Native_File_Chooser_Driver::type() const { - return( _btype ); -} - -// SET OPTIONS -void Fl_WinAPI_Native_File_Chooser_Driver::options(int val) { - _options = val; -} - -// GET OPTIONS -int Fl_WinAPI_Native_File_Chooser_Driver::options() const { - return(_options); -} - -// PRIVATE: SET ERROR MESSAGE -void Fl_WinAPI_Native_File_Chooser_Driver::errmsg(const char *val) { - _errmsg = strfree(_errmsg); - _errmsg = strnew(val); -} - -// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS -void Fl_WinAPI_Native_File_Chooser_Driver::clear_pathnames() { - if ( _pathnames ) { - while ( --_tpathnames >= 0 ) { - _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); - } - delete[] _pathnames; - _pathnames = NULL; - } - _tpathnames = 0; -} - -// SET A SINGLE PATHNAME -void Fl_WinAPI_Native_File_Chooser_Driver::set_single_pathname(const char *s) { - clear_pathnames(); - _pathnames = new char*[1]; - _pathnames[0] = strnew(s); - _tpathnames = 1; -} - -// ADD PATHNAME TO EXISTING ARRAY -void Fl_WinAPI_Native_File_Chooser_Driver::add_pathname(const char *s) { - if ( ! _pathnames ) { - // Create first element in array - ++_tpathnames; - _pathnames = new char*[_tpathnames]; - } else { - // Grow array by 1 - char **tmp = new char*[_tpathnames+1]; // create new buffer - memcpy((void*)tmp, (void*)_pathnames, - sizeof(char*)*_tpathnames); // copy old - delete[] _pathnames; // delete old - _pathnames = tmp; // use new - ++_tpathnames; - } - _pathnames[_tpathnames-1] = strnew(s); -} - -// FREE A PIDL (Pointer to IDentity List) -static void FreePIDL(LPITEMIDLIST pidl) { - IMalloc *imalloc = NULL; - if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) { - imalloc->Free(pidl); - imalloc->Release(); - imalloc = NULL; - } -} - -// CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS -void Fl_WinAPI_Native_File_Chooser_Driver::ClearOFN() { - // Free any previously allocated lpstrFile before zeroing out _ofn_ptr - if ( _ofn_ptr->lpstrFile ) { - delete[] _ofn_ptr->lpstrFile; - _ofn_ptr->lpstrFile = NULL; - } - if ( _ofn_ptr->lpstrInitialDir ) { - delete[] (TCHAR*) _ofn_ptr->lpstrInitialDir; //msvc6 compilation fix - _ofn_ptr->lpstrInitialDir = NULL; - } - _ofn_ptr->lpstrFilter = NULL; // (deleted elsewhere) - int temp = _ofn_ptr->nFilterIndex; // keep the filter_value - memset((void*)_ofn_ptr, 0, sizeof(OPENFILENAMEW)); - _ofn_ptr->lStructSize = sizeof(OPENFILENAMEW); - _ofn_ptr->nFilterIndex = temp; -} - -// CLEAR MICROSOFT BINF (BROWSER INFO) CLASS -void Fl_WinAPI_Native_File_Chooser_Driver::ClearBINF() { - if ( _binf_ptr->pidlRoot ) { - FreePIDL((ITEMIDLIST*)_binf_ptr->pidlRoot); - _binf_ptr->pidlRoot = NULL; - } - memset((void*)_binf_ptr, 0, sizeof(BROWSEINFOW)); -} - -// CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES -void Fl_WinAPI_Native_File_Chooser_Driver::Win2Unix(char *s) { - while ( (s=strchr(s,'\\')) ) *s = '/'; -} - -// CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES -void Fl_WinAPI_Native_File_Chooser_Driver::Unix2Win(char *s) { - while ( (s=strchr(s,'/')) ) *s = '\\'; -} - -// SEE IF PATH IS FRONT SLASH OR BACKSLASH STYLE -// Use this to preserve path style after windows dialog appears. -// If no slashes are specified, windows is assumed. -// If a mix of both path styles is used, windows is assumed. -// -bool Fl_WinAPI_Native_File_Chooser_Driver::IsUnixPath(const char *s) { - if ( !s ) return false; // NULL? - if ( strchr(s, '\\') ) return false; // windows style? - if ( strchr(s, '/') ) return true; // unix style? - return false; // no slashes? assume native windows -} - -// SAVE THE CURRENT WORKING DIRECTORY -// Returns a malloc()ed copy of the cwd that can -// later be freed with RestoreCWD(). May return 0 on error. -// -static char *SaveCWD() { - char *thecwd = 0; - DWORD thecwdsz = GetCurrentDirectory(0,0); - if ( thecwdsz > 0 ) { - thecwd = (char*)malloc(thecwdsz); - if (GetCurrentDirectory(thecwdsz, thecwd) == 0 ) { - free(thecwd); thecwd = 0; - } - } - return thecwd; -} - -// RESTORES THE CWD SAVED BY SaveCWD(), FREES STRING -// Always returns NULL (string was freed). -// -static void RestoreCWD(char *thecwd) { - if ( !thecwd ) return; - SetCurrentDirectory(thecwd); - free(thecwd); -} - -// SHOW FILE BROWSER -int Fl_WinAPI_Native_File_Chooser_Driver::showfile() { - bool unixpath = IsUnixPath(_directory) || IsUnixPath(_preset_file); // caller uses unix paths? - ClearOFN(); - clear_pathnames(); - size_t fsize = FNFC_MAX_PATH; - _ofn_ptr->Flags |= OFN_NOVALIDATE; // prevent disabling of front slashes - _ofn_ptr->Flags |= OFN_HIDEREADONLY; // hide goofy readonly flag - // USE NEW BROWSER - _ofn_ptr->Flags |= OFN_EXPLORER; // use newer explorer windows - _ofn_ptr->Flags |= OFN_ENABLESIZING; // allow window to be resized (hey, why not?) - _ofn_ptr->Flags |= OFN_NOCHANGEDIR; // XXX: docs say ineffective on XP/2K/NT, but set it anyway.. - - switch ( _btype ) { - case Fl_Native_File_Chooser::BROWSE_DIRECTORY: - case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: - case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: - abort(); // never happens: handled by showdir() - case Fl_Native_File_Chooser::BROWSE_FILE: - break; - case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: - _ofn_ptr->Flags |= OFN_ALLOWMULTISELECT; - break; - case Fl_Native_File_Chooser::BROWSE_SAVE_FILE: - if ( options() & Fl_Native_File_Chooser::SAVEAS_CONFIRM && type() == Fl_Native_File_Chooser::BROWSE_SAVE_FILE ) { - _ofn_ptr->Flags |= OFN_OVERWRITEPROMPT; - } - break; - } - // SPACE FOR RETURNED FILENAME - _ofn_ptr->lpstrFile = new WCHAR[fsize]; - _ofn_ptr->nMaxFile = (DWORD)(fsize-1); - _ofn_ptr->lpstrFile[0] = 0; - _ofn_ptr->lpstrFile[1] = 0; // dnull - // PARENT WINDOW - _ofn_ptr->hwndOwner = GetForegroundWindow(); - // DIALOG TITLE - if (_title) { - static WCHAR wtitle[200]; - wcsncpy(wtitle, utf8towchar(_title), 200); - wtitle[200-1] = 0; - _ofn_ptr->lpstrTitle = wtitle; - } else { - _ofn_ptr->lpstrTitle = NULL; - } - // FILTER - if (_parsedfilt != NULL) { // to convert a null-containing char string into a widechar string - // NEW - if ( !_wpattern ) _wpattern = new WCHAR[FNFC_MAX_PATH]; - const char *p = _parsedfilt; - while(*(p + strlen(p) + 1) != 0) p += strlen(p) + 1; - p += strlen(p) + 2; - MultiByteToWideChar(CP_UTF8, 0, _parsedfilt, (int) (p - _parsedfilt), _wpattern, FNFC_MAX_PATH); - _ofn_ptr->lpstrFilter = _wpattern; - } else { - _ofn_ptr->lpstrFilter = NULL; - } - // PRESET FILE - // If set, supercedes _directory. See KB Q86920 for details - // XXX: this doesn't preselect the item in the listview.. why? - // - if ( _preset_file ) { - // Temp copy of _dirname we can convert to windows path if needed - char *winpath = fl_strdup(_preset_file); - if ( unixpath ) Unix2Win(winpath); - size_t len = strlen(winpath); - if ( len >= _ofn_ptr->nMaxFile ) { - char msg[80]; - snprintf(msg, 80, "preset_file() filename is too long: %ld is >=%ld", (long)len, (long)fsize); - errmsg(msg); - return(-1); - } - wcscpy(_ofn_ptr->lpstrFile, utf8towchar(winpath)); - len = wcslen(_ofn_ptr->lpstrFile); - _ofn_ptr->lpstrFile[len+0] = 0; // multiselect needs dnull - _ofn_ptr->lpstrFile[len+1] = 0; - free(winpath); // free temp copy now that we have a new wchar - //wprintf(L"lpstrFile is '%ls'\n", (WCHAR*)(_ofn_ptr->lpstrFile)); - } - // PRESET DIR - // XXX: See KB Q86920 for doc bug: - // http://support.microsoft.com/default.aspx?scid=kb;en-us;86920 - // - if ( _directory ) { - // Temp copy of _dirname we can convert to windows path if needed - char *winpath = fl_strdup(_directory); - // Caller specified unix front slash path? - // If so, convert to backslashes; windows native browser mishandles unix style paths. - // We'll convert back to unix style when dialog completes. - // - if ( unixpath ) Unix2Win(winpath); - // Make a wide char version of potentially utf8 string - _ofn_ptr->lpstrInitialDir = new WCHAR[FNFC_MAX_PATH]; - wcscpy((WCHAR *)_ofn_ptr->lpstrInitialDir, utf8towchar(winpath)); - free(winpath); // free temp copy now that we have a new wchar - //wprintf(L"lpstrInitialDir is '%ls'\n", (WCHAR*)(_ofn_ptr->lpstrInitialDir)); - } - // SAVE THE CURRENT DIRECTORY - // See above warning (XXX) for OFN_NOCHANGEDIR - // - char *save_cwd = SaveCWD(); // must be freed with RestoreCWD() - // OPEN THE DIALOG WINDOW - int err; - if ( _btype == Fl_Native_File_Chooser::BROWSE_SAVE_FILE ) { - err = GetSaveFileNameW(_ofn_ptr); - } else { - err = GetOpenFileNameW(_ofn_ptr); - } - // GET EXTENDED ERROR - int exterr = CommDlgExtendedError(); - // RESTORE CURRENT DIRECTORY - RestoreCWD(save_cwd); save_cwd = 0; // also frees save_cwd - // ERROR OR CANCEL? - if ( err == 0 ) { - if ( exterr == 0 ) return(1); // user hit cancel - // Otherwise, an error occurred.. - char msg[80]; - snprintf(msg, 80, "CommDlgExtendedError() code=%d", exterr); - errmsg(msg); - return(-1); - } - // PREPARE PATHNAMES FOR RETURN - switch ( _btype ) { - case Fl_Native_File_Chooser::BROWSE_FILE: - case Fl_Native_File_Chooser::BROWSE_SAVE_FILE: - set_single_pathname(wchartoutf8(_ofn_ptr->lpstrFile)); - if ( unixpath ) Win2Unix(_pathnames[_tpathnames-1]); // preserve unix style path - break; - case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: { - // EXTRACT MULTIPLE FILENAMES - const WCHAR *dirname = _ofn_ptr->lpstrFile; - size_t dirlen = wcslen(dirname); - if ( dirlen > 0 ) { - // WALK STRING SEARCHING FOR 'DOUBLE-NULL' - // eg. "/dir/name\0foo1\0foo2\0foo3\0\0" - // - char pathname[FNFC_MAX_PATH]; - for ( const WCHAR *s = dirname + dirlen + 1; *s; s += (wcslen(s)+1)) { - // ISSUE #206: replace strncpy/cat with fl_strlcpy/cat - fl_strlcpy(pathname, wchartoutf8(dirname), FNFC_MAX_PATH); - fl_strlcat(pathname, "\\", FNFC_MAX_PATH); - fl_strlcat(pathname, wchartoutf8(s), FNFC_MAX_PATH); - add_pathname(pathname); - } - } - // XXX - // Work around problem where pasted forward-slash pathname - // into the file browser causes new "Explorer" interface - // not to grok forward slashes, passing back as a 'filename'..! - // - if ( _tpathnames == 0 ) { - add_pathname(wchartoutf8(dirname)); - } - // Caller specified unix path? Return unix paths - if ( unixpath ) { - for ( int t=0; t<_tpathnames; t++ ) { - Win2Unix(_pathnames[t]); - } - } - break; - } - case Fl_Native_File_Chooser::BROWSE_DIRECTORY: - case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: - case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: - abort(); // never happens: handled by showdir() - } - return(0); -} - -// Used by SHBrowseForFolder(), sets initial selected dir. -// Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes -// Subject: How to specify to select an initial folder .." -// -static int CALLBACK Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data) { - switch (msg) { - case BFFM_INITIALIZED: - if (data) ::SendMessageW(win, BFFM_SETSELECTIONW, TRUE, data); - break; - case BFFM_SELCHANGED: - TCHAR path[FNFC_MAX_PATH]; - if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) { - ::SendMessage(win, BFFM_ENABLEOK, 0, 1); - } else { - // disable ok button if not a path - ::SendMessage(win, BFFM_ENABLEOK, 0, 0); - } - break; - case BFFM_VALIDATEFAILED: - // we could pop up an annoying message here. - // also needs set ulFlags |= BIF_VALIDATE - break; - default: - break; - } - return(0); -} - -// SHOW DIRECTORY BROWSER -int Fl_WinAPI_Native_File_Chooser_Driver::showdir() { - bool unixpath = IsUnixPath(_directory); // caller uses unix paths? - // initialize OLE only once - fl_open_display(); // init needed by BIF_USENEWUI - ClearBINF(); - clear_pathnames(); - // PARENT WINDOW - _binf_ptr->hwndOwner = GetForegroundWindow(); - // DIALOG TITLE - //_binf_ptr->lpszTitle = _title ? _title : NULL; - if (_title) { - static WCHAR wtitle[256]; - wcsncpy(wtitle, utf8towchar(_title), 256); - wtitle[255] = 0; - _binf_ptr->lpszTitle = wtitle; - } else { - _binf_ptr->lpszTitle = NULL; - } - - // FLAGS - _binf_ptr->ulFlags = 0; // initialize - - // TBD: make sure matches to runtime system, if need be. - //(what if _WIN32_IE doesn't match system? does the program not run?) - // - // TBD: match all 3 types of directories - // - // NOTE: *Don't* use BIF_SHAREABLE. It /disables/ mapped network shares - // from being visible in BROWSE_DIRECTORY mode. Walter Garm's comments: - // - // --- Garms, Walter (GE EntSol, Security) wrote: - // With your help I was able to solve the problem of the network drives. - // For Version 6.0, at least, the BIF_SHAREABLE flag seems to have the - // opposite sense: With BIF_SHAREABLE not set I see the mapped network - // drives, and with BIF_SHAREABLE set I do not. - // --- - -#if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0 - if ( _btype == Fl_Native_File_Chooser::BROWSE_DIRECTORY ) _binf_ptr->ulFlags |= BIF_NONEWFOLDERBUTTON; - _binf_ptr->ulFlags |= BIF_USENEWUI | BIF_RETURNONLYFSDIRS; -#elif defined(BIF_USENEWUI) // Version 5.0 - if ( _btype == Fl_Native_File_Chooser::BROWSE_DIRECTORY ) _binf_ptr->ulFlags |= BIF_EDITBOX; - else if ( _btype == Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY ) _binf_ptr->ulFlags |= BIF_USENEWUI; - _binf_ptr->ulFlags |= BIF_RETURNONLYFSDIRS; -#elif defined(BIF_EDITBOX) // Version 4.71 - _binf_ptr->ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX; -#else // Version Old - _binf_ptr->ulFlags |= BIF_RETURNONLYFSDIRS; -#endif - - // BUFFER - //char displayname[FNFC_MAX_PATH]; - WCHAR displayname[FNFC_MAX_PATH]; - _binf_ptr->pszDisplayName = displayname; - - // PRESET DIR - WCHAR presetname[FNFC_MAX_PATH]; - if ( _directory ) { - // Temp copy of _dirname we can convert to windows path if needed - char *winpath = fl_strdup(_directory); - // Caller specified unix front slash path? - // If so, convert to backslashes; windows native browser mishandles unix style paths. - // We'll convert back to unix style when dialog completes. - // - if ( unixpath ) Unix2Win(winpath); - // Wide char version of potentially utf8 string - wcsncpy(presetname, utf8towchar(winpath), FNFC_MAX_PATH); - free(winpath); // free temp copy now that we have a new wchar - presetname[FNFC_MAX_PATH-1] = 0; // dnull - presetname[FNFC_MAX_PATH-2] = 0; - _binf_ptr->lParam = (LPARAM)presetname; - //wprintf(L"presetname is '%ls'\n", (WCHAR*)(presetname)); - } - else _binf_ptr->lParam = 0; - _binf_ptr->lpfn = Dir_CB; - // OPEN BROWSER - LPITEMIDLIST pidl = SHBrowseForFolderW(_binf_ptr); - // CANCEL? - if ( pidl == NULL ) return(1); - - // GET THE PATHNAME(S) THE USER SELECTED - // TBD: expand NetHood shortcuts from this PIDL?? - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp - - WCHAR path[FNFC_MAX_PATH]; - if ( SHGetPathFromIDListW(pidl, path) ) { - add_pathname(wchartoutf8(path)); - if ( unixpath ) Win2Unix(_pathnames[_tpathnames-1]); // preserve unix style path - } - FreePIDL(pidl); - if ( !wcslen(path) ) return(1); // don't return empty pathnames - return(0); -} - -// RETURNS: -// 0 - user picked a file -// 1 - user cancelled -// -1 - failed; errmsg() has reason -// -int Fl_WinAPI_Native_File_Chooser_Driver::show() { - int retval; - if ( _btype == Fl_Native_File_Chooser::BROWSE_DIRECTORY || - _btype == Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY || - _btype == Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY ) { - retval = showdir(); - } else { - retval = showfile(); - } - // restore the correct state of mouse buttons and keyboard modifier keys (STR #3221) - HWND h = GetForegroundWindow(); - if (h) { - WNDPROC windproc = (WNDPROC)GetWindowLongPtrW(h, GWLP_WNDPROC); - CallWindowProc(windproc, h, WM_ACTIVATEAPP, 1, 0); - } - return retval; -} - -// RETURN ERROR MESSAGE -const char *Fl_WinAPI_Native_File_Chooser_Driver::errmsg() const { - return(_errmsg ? _errmsg : "No error"); -} - -// GET FILENAME -const char* Fl_WinAPI_Native_File_Chooser_Driver::filename() const { - if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); - return(""); -} - -// GET FILENAME FROM LIST OF FILENAMES -const char* Fl_WinAPI_Native_File_Chooser_Driver::filename(int i) const { - if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); - return(""); -} - -// GET TOTAL FILENAMES CHOSEN -int Fl_WinAPI_Native_File_Chooser_Driver::count() const { - return(_tpathnames); -} - -// PRESET PATHNAME -// Can be NULL if no preset is desired. -// -void Fl_WinAPI_Native_File_Chooser_Driver::directory(const char *val) { - _directory = strfree(_directory); - _directory = strnew(val); -} - -// GET PRESET PATHNAME -// Can return NULL if none set. -// -const char *Fl_WinAPI_Native_File_Chooser_Driver::directory() const { - return(_directory); -} - -// SET TITLE -// Can be NULL if no title desired. -// -void Fl_WinAPI_Native_File_Chooser_Driver::title(const char *val) { - _title = strfree(_title); - _title = strnew(val); -} - -// GET TITLE -// Can return NULL if none set. -// -const char *Fl_WinAPI_Native_File_Chooser_Driver::title() const { - return(_title); -} - -// SET FILTER -// Can be NULL if no filter needed -// -void Fl_WinAPI_Native_File_Chooser_Driver::filter(const char *val) { - _filter = strfree(_filter); - clear_filters(); - if ( val ) { - _filter = strnew(val); - parse_filter(_filter); - } - add_filter("All Files", "*.*"); // always include 'all files' option - -#ifdef DEBUG - dnullprint(_parsedfilt); -#endif /*DEBUG*/ -} - -// GET FILTER -// Can return NULL if none set. -// -const char *Fl_WinAPI_Native_File_Chooser_Driver::filter() const { - return(_filter); -} - -// CLEAR FILTERS -void Fl_WinAPI_Native_File_Chooser_Driver::clear_filters() { - _nfilters = 0; - _parsedfilt = strfree(_parsedfilt); -} - -// ADD A FILTER -void Fl_WinAPI_Native_File_Chooser_Driver::add_filter(const char *name_in, // name of filter (optional: can be null) - const char *winfilter) { // windows style filter (eg. "*.cxx;*.h") - // No name? Make one.. - char name[1024]; - if ( !name_in || name_in[0] == '\0' ) { - snprintf(name, sizeof(name), "%.*s Files", int(sizeof(name)-10), winfilter); - } else { - if ((strlen(name_in)+strlen(winfilter)+3) < sizeof(name)) { - snprintf(name, sizeof(name), "%s (%s)", name_in, winfilter); - } else { - snprintf(name, sizeof(name), "%.*s", int(sizeof(name))-1, name_in); - } - } - dnullcat(_parsedfilt, name); - dnullcat(_parsedfilt, winfilter); - _nfilters++; - //DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter); -} - -// RETURN HOW MANY DIFFERENT FILTERS WERE SPECIFIED -// In: "foo.[CH]" or "foo.{C,H}" -// Out: 2 -// -static int count_filters(const char *filter) { - int count = 0; - char mode = 0; - const char *in = filter; - while (*in) { - switch(*in) { - case '\\': // escape next character - ++in; if ( *in == 0 ) continue; // skip escape. EOL? done - ++in; // skip escaped char - continue; - case LCURLY_CHR: // start "{aaa,bbb}" - mode = *in; // set mode, parse over curly - ++count; // at least +1 wildcard - break; - case RCURLY_CHR: // end "{aaa,bbb}" - if ( mode == LCURLY_CHR ) // disable curly mode (if on) - mode = 0; - break; - case LBRACKET_CHR: // start "[xyz]" - mode = *in; // set mode, parse over bracket - break; - case RBRACKET_CHR: // end "[xyz]" - if ( mode == LBRACKET_CHR ) // disable bracket mode (if on) - mode = 0; - break; - default: // any other char - switch (mode) { // handle {} or [] modes - case LCURLY_CHR: // handle "{aaa,bbb}" - if (*in==',' || *in=='|') // ',' and '|' adds filters - ++count; - break; - case LBRACKET_CHR: // handle "[xyz]" - ++count; // all chars in []'s add new filter - break; - } - break; - } - ++in; // parse past char - } - return count > 0 ? count : 1; // return at least 1 -} - -// Convert FLTK style pattern matches to windows 'double-null' pattern -// Returns with the parsed double-null result in '_parsedfilt'. -// -// Handles: -// IN OUT -// ----------- ----------------------------- -// *.{ma,mb} "*.{ma,mb} Files\0*.ma;*.mb\0\0" -// *.[abc] "*.[abc] Files\0*.a;*.b;*.c\0\0" -// *.txt "*.txt Files\0*.txt\0\0" -// C Files\t*.[ch] "C Files\0*.c;*.h\0\0" -// -// Example: -// IN: "*.{ma,mb}" -// OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0" -// --------------- --------- --------- --- -// | | | | -// Title Wildcards Title Wildcards -// -// Parsing Mode: -// IN:"C Files\t*.{cxx,h}" -// ||||||| ||||||||| -// mode: nnnnnnn ww{{{{{{{ -// \_____/ \_______/ -// Name Wildcard -// -void Fl_WinAPI_Native_File_Chooser_Driver::parse_filter(const char *in) { - clear_filters(); - if ( ! in || in[0] == '\0' ) return; - - int has_name = strchr(in, '\t') ? 1 : 0; - char mode = has_name ? 'n' : 'w'; // parse mode: n=name, w=wildcard - - // whatever input string is, our output won't be much longer in length.. - // use double length just for safety. - size_t slen = strlen(in); - char *wildprefix = new char[(slen+1)*2]; wildprefix[0] = 0; - char *comp = new char[(slen+1)*2]; comp[0] = 0; - char *name = new char[(slen+1)*2]; name[0] = 0; - - // Init - int nwildcards = 0; - int maxfilters = count_filters(in) + 1; // count wildcard seps - char **wildcards = new char*[maxfilters]; // parsed wildcards (can be several) - int t; - for ( t=0; t name=<%s> wildprefix=<%s> nwildcards=%d wildcards[n]=<%s>\n", - // *in, mode, name, wildprefix, nwildcards, wildcards[nwildcards]); - - switch (*in) { - case ',': - case '|': - if ( mode == LCURLY_CHR ) { - // create new wildcard, copy in prefix - strcat(wildcards[nwildcards++], wildprefix); - continue; - } else { - goto regchar; - } - continue; - - // FINISHED PARSING A NAME? - case '\t': - if ( mode != 'n' ) goto regchar; - // finish parsing name? switch to wildcard mode - mode = 'w'; - break; - - // ESCAPE NEXT CHAR - case '\\': - ++in; - goto regchar; - - // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? - case '\r': - case '\n': - case '\0': - { - if ( mode == 'w' ) { // finished parsing wildcard? - if ( nwildcards == 0 ) { - strcpy(wildcards[nwildcards++], wildprefix); - } - // Append wildcards in Microsoft's "*.one;*.two" format - comp[0] = 0; - for ( t=0; t 0 ) { - chrcat(wildcards[nwildcards-1], *in); - } - continue; - - case 'n': - chrcat(name, *in); - continue; - - case 'w': - chrcat(wildprefix, *in); - for ( t=0; tnFilterIndex = i + 1; -} - -// RETURN VALUE OF 'CURRENTLY SELECTED FILTER' -int Fl_WinAPI_Native_File_Chooser_Driver::filter_value() const { - return(_ofn_ptr->nFilterIndex ? _ofn_ptr->nFilterIndex-1 : _nfilters+1); -} - -// PRESET FILENAME FOR 'SAVE AS' CHOOSER -void Fl_WinAPI_Native_File_Chooser_Driver::preset_file(const char* val) { - _preset_file = strfree(_preset_file); - _preset_file = strnew(val); -} - -// GET PRESET FILENAME FOR 'SAVE AS' CHOOSER -const char* Fl_WinAPI_Native_File_Chooser_Driver::preset_file() const { - return(_preset_file); -} - -int Fl_WinAPI_Native_File_Chooser_Driver::filters() const { - return(_nfilters); -} - -static char *wchartoutf8(LPCWSTR in) { - static char *out = NULL; - static int lchar = 0; - if (in == NULL)return NULL; - int utf8len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); - if (utf8len > lchar) { - lchar = utf8len; - out = (char *)realloc(out, lchar * sizeof(char)); - } - WideCharToMultiByte(CP_UTF8, 0, in, -1, out, utf8len, NULL, NULL); - return out; -} - -static LPCWSTR utf8towchar(const char *in) { - static WCHAR *wout = NULL; - static int lwout = 0; - if (in == NULL)return NULL; - int wlen = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0); - if (wlen > lwout) { - lwout = wlen; - wout = (WCHAR *)realloc(wout, lwout * sizeof(WCHAR)); - } - MultiByteToWideChar(CP_UTF8, 0, in, -1, wout, wlen); - return wout; -} - -#endif /* !FL_DOXYGEN */ diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm deleted file mode 100644 index 02e66ed43..000000000 --- a/src/Fl_cocoa.mm +++ /dev/null @@ -1,4793 +0,0 @@ -// -// macOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -extern "C" { -#include -} - -#include -#include -#include -#include "Fl_Window_Driver.H" -#include "Fl_Screen_Driver.H" -#include "Fl_Timeout.h" -#include -#include -#include -#include -#include -#include -#include "drivers/Quartz/Fl_Quartz_Graphics_Driver.H" -#include "drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H" -#include "drivers/Cocoa/Fl_Cocoa_Screen_Driver.H" -#include "drivers/Cocoa/Fl_Cocoa_Window_Driver.H" -#include "drivers/Darwin/Fl_Darwin_System_Driver.H" -#include "drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H" -#include "print_button.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 -# error macOS SDK and deployment target version 10.7 or higher is required. -// Note: change also the warning in Fl_Darwin_System_Driver::calc_mac_os_version() below -#endif - -#import -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 -# import -#endif - -// #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING -#ifdef DEBUG_SELECT -#include // testing -#define DEBUGMSG(msg) if ( msg ) fprintf(stderr, msg); -#define DEBUGPERRORMSG(msg) if ( msg ) perror(msg) -#define DEBUGTEXT(txt) txt -#else -#define DEBUGMSG(msg) -#define DEBUGPERRORMSG(msg) -#define DEBUGTEXT(txt) NULL -#endif /*DEBUG_SELECT*/ - -// external functions -extern void fl_fix_focus(); -extern int fl_send_system_handlers(void *e); - -// forward definition of functions in this file -// converting cr lf converter function -static void createAppleMenu(void); -static void cocoaMouseHandler(NSEvent *theEvent); -#if defined(FLTK_HAVE_PEN_SUPPORT) -static bool cocoaTabletHandler(NSEvent *theEvent, bool lock); -extern bool fl_cocoa_tablet_handler(NSEvent*, Fl_Window*); -#endif -static void clipboard_check(void); -static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h); -static NSBitmapImageRep* rect_to_NSBitmapImageRep_subwins(Fl_Window *win, int x, int y, int w, int h, bool capture_subwins); -static void drain_dropped_files_list(void); -static NSPoint FLTKtoCocoa(Fl_Window *win, int x, int y, int H); -static int get_window_frame_sizes(Fl_Window *win, int *pbx = NULL, int *pby = NULL); - -int fl_mac_os_version = Fl_Darwin_System_Driver::calc_mac_os_version(); // the version number of the running Mac OS X (e.g., 100604 for 10.6.4) - -// public variables -void *fl_capture = 0; // (NSWindow*) we need this to compensate for a missing(?) mouse capture -FLWindow *fl_window; - -// forward declarations of variables in this file -static int main_screen_height; // height of menubar-containing screen used to convert between Cocoa and FLTK global screen coordinates -// through_drawRect = YES means the drawRect: message was sent to the view, -// thus the graphics context was prepared by the system -static BOOL through_drawRect = NO; -// through_Fl_X_flush = YES means Fl_Cocoa_Window_Driver::flush() was called -static BOOL through_Fl_X_flush = NO; -static BOOL views_use_CA = NO; // YES means views are layer-backed, as on macOS 10.14 when linked with SDK 10.14 -static int im_enabled = -1; - -// OS version-dependent pasteboard type names. -// the next 5 deprecation/availability warnings can be legitimately ignored -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#pragma clang diagnostic ignored "-Wunguarded-availability" -static NSString *TIFF_pasteboard_type = NSPasteboardTypeTIFF; -static NSString *PDF_pasteboard_type = NSPasteboardTypePDF; -static NSString *PICT_pasteboard_type = @"com.apple.pict"; -static NSString *UTF8_pasteboard_type = NSPasteboardTypeString; -static NSString *fl_filenames_pboard_type = -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - (fl_mac_os_version >= 101300 ? NSPasteboardTypeFileURL : NSFilenamesPboardType); -#else - NSFilenamesPboardType; -#endif -#pragma clang diagnostic pop - -static bool in_nsapp_run = false; // true during execution of [NSApp run] -static NSMutableArray *dropped_files_list = nil; // list of files dropped at app launch -typedef void (*open_cb_f_type)(const char *); -static Fl_Window *starting_moved_window = NULL; // the moved window which brings its subwins with it - -enum { FLTKTimerEvent = 1, FLTKDataReadyEvent }; - -// Carbon functions and definitions - -typedef void *TSMDocumentID; - -extern "C" enum { - kTSMDocumentEnabledInputSourcesPropertyTag = 'enis' // from Carbon/TextServices.h -}; - -// Undocumented voodoo. Taken from Mozilla. -static const int smEnableRomanKybdsOnly = -23; - -typedef TSMDocumentID (*TSMGetActiveDocument_type)(void); -static TSMGetActiveDocument_type TSMGetActiveDocument; -typedef OSStatus (*TSMSetDocumentProperty_type)(TSMDocumentID, OSType, UInt32, void*); -static TSMSetDocumentProperty_type TSMSetDocumentProperty; -typedef OSStatus (*TSMRemoveDocumentProperty_type)(TSMDocumentID, OSType); -static TSMRemoveDocumentProperty_type TSMRemoveDocumentProperty; -typedef CFArrayRef (*TISCreateInputSourceList_type)(CFDictionaryRef, Boolean); -static TISCreateInputSourceList_type TISCreateInputSourceList; -static CFStringRef kTISTypeKeyboardLayout; -static CFStringRef kTISPropertyInputSourceType; - -typedef void (*KeyScript_type)(short); -static KeyScript_type KeyScript; - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 -const NSInteger NSControlStateValueOn = NSOnState; -const NSInteger NSControlStateValueOff = NSOffState; -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -const NSUInteger NSEventModifierFlagCommand = NSCommandKeyMask; -const NSUInteger NSEventModifierFlagOption = NSAlternateKeyMask; -const NSUInteger NSEventModifierFlagControl = NSControlKeyMask; -const NSUInteger NSEventModifierFlagShift = NSShiftKeyMask; -const NSUInteger NSEventModifierFlagCapsLock = NSAlphaShiftKeyMask; - -const NSEventType NSEventTypeLeftMouseDown = NSLeftMouseDown; -const NSEventType NSEventTypeRightMouseDown = NSRightMouseDown; -const NSEventType NSEventTypeOtherMouseDown = NSOtherMouseDown; -const NSEventType NSEventTypeLeftMouseUp = NSLeftMouseUp; -const NSEventType NSEventTypeRightMouseUp = NSRightMouseUp; -const NSEventType NSEventTypeOtherMouseUp = NSOtherMouseUp; -const NSEventType NSEventTypeLeftMouseDragged = NSLeftMouseDragged; -const NSEventType NSEventTypeRightMouseDragged = NSRightMouseDragged; -const NSEventType NSEventTypeOtherMouseDragged = NSOtherMouseDragged; -const NSEventType NSEventTypeMouseMoved = NSMouseMoved; -const NSEventType NSEventTypeMouseEntered = NSMouseEntered; -const NSEventType NSEventTypeMouseExited = NSMouseExited; -const NSEventType NSEventTypeKeyUp = NSKeyUp; -const NSEventType NSEventTypeApplicationDefined = NSApplicationDefined; - -const NSUInteger NSWindowStyleMaskResizable = NSResizableWindowMask; -const NSUInteger NSWindowStyleMaskBorderless = NSBorderlessWindowMask; -const NSUInteger NSWindowStyleMaskMiniaturizable = NSMiniaturizableWindowMask; -const NSUInteger NSWindowStyleMaskClosable = NSClosableWindowMask; -const NSUInteger NSWindowStyleMaskTitled = NSTitledWindowMask; -const NSUInteger NSWindowStyleMaskFullScreen = NSFullScreenWindowMask; - -const NSUInteger NSEventMaskAny = NSAnyEventMask; -const NSUInteger NSEventMaskSystemDefined = NSSystemDefinedMask; - -const NSUInteger NSBitmapFormatAlphaFirst = NSAlphaFirstBitmapFormat; -const NSUInteger NSBitmapFormatAlphaNonpremultiplied = NSAlphaNonpremultipliedBitmapFormat; -const short NSEventSubtypeTabletProximity = NSTabletProximityEventSubtype; -const short NSEventSubtypeTabletPoint = NSTabletPointEventSubtype; -const NSUInteger NSEventModifierFlagFunction = NSFunctionKeyMask; -#endif - -/* - * Mac keyboard lookup table - */ -static unsigned short* macKeyLookUp = NULL; - -/* - * convert the current mouse chord into the FLTK modifier state - */ -static unsigned int mods_to_e_state( NSUInteger mods ) -{ - unsigned int state = 0; - if ( mods & NSEventModifierFlagCommand ) state |= FL_META; - if ( mods & NSEventModifierFlagOption ) state |= FL_ALT; - if ( mods & NSEventModifierFlagControl ) state |= FL_CTRL; - if ( mods & NSEventModifierFlagShift ) state |= FL_SHIFT; - if ( mods & NSEventModifierFlagCapsLock ) state |= FL_CAPS_LOCK; - unsigned int ret = ( Fl::e_state & 0xff000000 ) | state; - Fl::e_state = ret; - //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods ); - return ret; -} - -// these pointers are set by the Fl::lock() function: -static void nothing() {} -void (*fl_lock_function)() = nothing; -void (*fl_unlock_function)() = nothing; - -// -// Select interface -- how it's implemented: -// When the user app configures one or more file descriptors to monitor -// with Fl::add_fd(), we start a separate thread to select() the data, -// sending a custom OSX 'FLTK data ready event' to the parent thread's -// RunApplicationLoop(), so that it triggers the data ready callbacks -// in the parent thread. -erco 04/04/04 -// -#define POLLIN 1 -#define POLLOUT 4 -#define POLLERR 8 - -// Class to handle select() 'data ready' -class DataReady -{ - struct FD - { - int fd; - short events; - void (*cb)(int, void*); - void* arg; - }; - int nfds, fd_array_size; - FD *fds; - pthread_t tid; // select()'s thread id - - // Data that needs to be locked (all start with '_') - pthread_mutex_t _datalock; // data lock - fd_set _fdsets[3]; // r/w/x sets user wants to monitor - int _maxfd; // max fd count to monitor - int _cancelpipe[2]; // pipe used to help cancel thread - -public: - DataReady() - { - nfds = 0; - fd_array_size = 0; - fds = 0; - tid = 0; - - pthread_mutex_init(&_datalock, NULL); - FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]); - _cancelpipe[0] = _cancelpipe[1] = 0; - _maxfd = -1; - } - - ~DataReady() - { - CancelThread(DEBUGTEXT("DESTRUCTOR\n")); - if (fds) { free(fds); fds = 0; } - nfds = 0; - } - - // Locks - // The convention for locks: volatile vars start with '_', - // and must be locked before use. Locked code is prefixed - // with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco - // - void DataLock() { pthread_mutex_lock(&_datalock); } - void DataUnlock() { pthread_mutex_unlock(&_datalock); } - - // Accessors - int IsThreadRunning() { return(tid ? 1 : 0); } - int GetNfds() { return(nfds); } - int GetCancelPipe(int ix) { return(_cancelpipe[ix]); } - fd_set GetFdset(int ix) { return(_fdsets[ix]); } - - // Methods - void AddFD(int n, int events, void (*cb)(int, void*), void *v); - void RemoveFD(int n, int events); - int CheckData(fd_set& r, fd_set& w, fd_set& x); - void HandleData(fd_set& r, fd_set& w, fd_set& x); - static void* DataReadyThread(void *self); - void StartThread(void); - void CancelThread(const char *reason); -}; - -static DataReady dataready; - -void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v) -{ - RemoveFD(n, events); - int i = nfds++; - if (i >= fd_array_size) - { - fl_open_display(); // necessary for NSApp to be defined and the event loop to work - FD *temp; - fd_array_size = 2*fd_array_size+1; - if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); } - else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); } - if (!temp) return; - fds = temp; - } - fds[i].cb = cb; - fds[i].arg = v; - fds[i].fd = n; - fds[i].events = events; - DataLock(); - /*LOCK*/ if (events & POLLIN) FD_SET(n, &_fdsets[0]); - /*LOCK*/ if (events & POLLOUT) FD_SET(n, &_fdsets[1]); - /*LOCK*/ if (events & POLLERR) FD_SET(n, &_fdsets[2]); - /*LOCK*/ if (n > _maxfd) _maxfd = n; - DataUnlock(); -} - -// Remove an FD from the array -void DataReady::RemoveFD(int n, int events) -{ - int i,j; - _maxfd = -1; // recalculate maxfd on the fly - for (i=j=0; i _maxfd) _maxfd = fds[i].fd; - // move it down in the array if necessary: - if (jDataLock(); - /*LOCK*/ int maxfd = self->_maxfd; - /*LOCK*/ fd_set r = self->GetFdset(0); - /*LOCK*/ fd_set w = self->GetFdset(1); - /*LOCK*/ fd_set x = self->GetFdset(2); - /*LOCK*/ int cancelpipe = self->GetCancelPipe(0); - /*LOCK*/ if ( cancelpipe > maxfd ) maxfd = cancelpipe; - /*LOCK*/ FD_SET(cancelpipe, &r); // add cancelpipe to fd's to watch - /*LOCK*/ FD_SET(cancelpipe, &x); - self->DataUnlock(); - // timeval t = { 1000, 0 }; // 1000 seconds; - timeval t = { 2, 0 }; // HACK: 2 secs prevents 'hanging' problem - int ret = ::select(maxfd+1, &r, &w, &x, &t); - pthread_testcancel(); // OSX 10.0.4 and older: needed for parent to cancel - switch ( ret ) { - case 0: // NO DATA - continue; - case -1: // ERROR - { - DEBUGPERRORMSG("CHILD THREAD: select() failed"); - return(NULL); // error? exit thread - } - default: // DATA READY - { - if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x)) // cancel? - { return(NULL); } // just exit - DEBUGMSG("CHILD THREAD: DATA IS READY\n"); - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 context:NULL subtype:FLTKDataReadyEvent data1:0 data2:0]; - [NSApp postEvent:event atStart:NO]; - [localPool release]; - return(NULL); // done with thread - } - } - } -} - -// START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE -void DataReady::StartThread(void) -{ - CancelThread(DEBUGTEXT("STARTING NEW THREAD\n")); - DataLock(); - /*LOCK*/ pipe(_cancelpipe); // pipe for sending cancel msg to thread - DataUnlock(); - DEBUGMSG("*** START THREAD\n"); - pthread_create(&tid, NULL, DataReadyThread, (void*)this); -} - -// CANCEL 'DATA READY' THREAD, CLOSE PIPE -void DataReady::CancelThread(const char *reason) -{ - if ( tid ) { - DEBUGMSG("*** CANCEL THREAD: "); - DEBUGMSG(reason); - if ( pthread_cancel(tid) == 0 ) { // cancel first - DataLock(); - /*LOCK*/ write(_cancelpipe[1], "x", 1); // wake thread from select - DataUnlock(); - pthread_join(tid, NULL); // wait for thread to finish - } - tid = 0; - DEBUGMSG("(JOINED) OK\n"); - } - // Close pipe if open - DataLock(); - /*LOCK*/ if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; } - /*LOCK*/ if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; } - DataUnlock(); -} - -void Fl_Darwin_System_Driver::add_fd( int n, int events, void (*cb)(int, void*), void *v ) -{ - dataready.AddFD(n, events, cb, v); -} - -void Fl_Darwin_System_Driver::add_fd(int fd, void (*cb)(int, void*), void* v) -{ - dataready.AddFD(fd, POLLIN, cb, v); -} - -void Fl_Darwin_System_Driver::remove_fd(int n, int events) -{ - dataready.RemoveFD(n, events); -} - -void Fl_Darwin_System_Driver::remove_fd(int n) -{ - dataready.RemoveFD(n, -1); -} - -/* - * Check if there is actually a message pending - */ -int Fl_Darwin_System_Driver::ready() - -{ - NSEvent *retval = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate dateWithTimeIntervalSinceNow:0] - inMode:NSDefaultRunLoopMode - dequeue:NO]; - return retval != nil; -} - - -static void processFLTKEvent(void) { - fl_lock_function(); - dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n")); - - // CHILD THREAD TELLS US DATA READY - // Check to see what's ready, and invoke user's cb's - // - fd_set r,w,x; - switch(dataready.CheckData(r,w,x)) { - case 0: // NO DATA - break; - case -1: // ERROR - break; - default: // DATA READY - dataready.HandleData(r,w,x); - break; - } - fl_unlock_function(); - return; -} - - -/* - * break the current event loop - */ -void Fl_Cocoa_Screen_Driver::breakMacEventLoop() -{ - NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 timestamp:0 - windowNumber:0 context:NULL - subtype:FLTKTimerEvent - data1:0 - data2:0]; - [NSApp postEvent:event atStart:NO]; -} - - -@interface FLWindow : NSWindow { - Fl_Window *w; -} -- (FLWindow*)initWithFl_W:(Fl_Window *)flw - contentRect:(NSRect)rect - styleMask:(NSUInteger)windowStyle; -- (Fl_Window *)getFl_Window; -- (void)recursivelySendToSubwindows:(SEL)sel applyToSelf:(BOOL)b; -- (void)setSubwindowFrame; -- (void)checkSubwindowFrame; -- (void)waitForExpose; -- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen; -- (NSPoint)convertBaseToScreen:(NSPoint)aPoint; -- (NSBitmapImageRep*)rect_to_NSBitmapImageRep:(Fl_Rect*)r; -- (void)makeKeyWindow; -@end - - -@interface FLView : NSView { - BOOL in_key_event; // YES means keypress is being processed by handleEvent - BOOL need_handle; // YES means Fl::handle(FL_KEYBOARD,) is needed after handleEvent processing - NSInteger identifier; - NSRange selectedRange; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -@public - CGContextRef aux_bitmap; // all drawing to view goes there and is finally copied to the CALayer -#endif -} -+ (void)prepareEtext:(NSString*)aString; -+ (void)concatEtext:(NSString*)aString; -- (BOOL)process_keydown:(NSEvent*)theEvent; -- (id)initWithFrame:(NSRect)frameRect; -- (void)drawRect:(NSRect)rect; -- (BOOL)acceptsFirstResponder; -- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; -- (void)resetCursorRects; -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent; -- (void)mouseUp:(NSEvent *)theEvent; -- (void)rightMouseUp:(NSEvent *)theEvent; -- (void)otherMouseUp:(NSEvent *)theEvent; -- (void)mouseDown:(NSEvent *)theEvent; -- (void)rightMouseDown:(NSEvent *)theEvent; -- (void)otherMouseDown:(NSEvent *)theEvent; -- (void)mouseMoved:(NSEvent *)theEvent; -- (void)mouseEntered:(NSEvent *)theEvent; -- (void)mouseExited:(NSEvent *)theEvent; -- (void)mouseDragged:(NSEvent *)theEvent; -- (void)rightMouseDragged:(NSEvent *)theEvent; -- (void)otherMouseDragged:(NSEvent *)theEvent; -- (void)scrollWheel:(NSEvent *)theEvent; -- (void)magnifyWithEvent:(NSEvent *)theEvent; -- (void)keyDown:(NSEvent *)theEvent; -- (void)keyUp:(NSEvent *)theEvent; -- (void)flagsChanged:(NSEvent *)theEvent; -- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender; -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; -- (BOOL)performDragOperation:(id )sender; -- (void)draggingExited:(id < NSDraggingInfo >)sender; -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; -- (void)updateTrackingAreas; -- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context; -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation; -- (BOOL)did_view_resolution_change; -#if defined(FLTK_HAVE_PEN_SUPPORT) -- (void)tabletProximity:(NSEvent *)theEvent; -- (void)tabletPoint:(NSEvent *)theEvent; -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -- (void)create_aux_bitmap:(CGContextRef)gc retina:(BOOL)r; -- (void)reset_aux_bitmap; -#endif -@end - - -@implementation FLWindow -- (void)close -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (views_use_CA) [(FLView*)[self contentView] reset_aux_bitmap]; -#endif - [[self standardWindowButton:NSWindowDocumentIconButton] setImage:nil]; - [super close]; - while (true) { - NSArray *a = [[self contentView] trackingAreas]; - if ([a count] == 0) break; - NSTrackingArea *ta = (NSTrackingArea*)[a objectAtIndex:0]; - [[self contentView] removeTrackingArea:ta]; - } - // when a fullscreen window is closed, windowDidResize may be sent after the close message was sent - // and before the FLWindow receives the final dealloc message - w = NULL; -} -- (NSPoint)convertBaseToScreen:(NSPoint)aPoint -{ - NSRect r = [self convertRectToScreen:NSMakeRect(aPoint.x, aPoint.y, 0, 0)]; - return r.origin; -} -- (FLWindow*)initWithFl_W:(Fl_Window *)flw - contentRect:(NSRect)rect - styleMask:(NSUInteger)windowStyle -{ - self = [super initWithContentRect:rect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO]; - if (self) { - w = flw; - [self setRestorable:NO]; - } - return self; -} -- (Fl_Window *)getFl_Window -{ - return w; -} - -- (BOOL)canBecomeKeyWindow -{ - if (Fl::modal_ && (Fl::modal_ != w)) - return NO; // prevent the caption to be redrawn as active on click - // when another modal window is currently the key win - return !(!w || w->output() || w->tooltip_window() || w->menu_window() || w->parent()); -} - -- (BOOL)canBecomeMainWindow -{ - if (Fl::modal_ && (Fl::modal_ != w)) - return NO; // prevent the caption to be redrawn as active on click - // when another modal window is currently the key win - - return !(!w || w->tooltip_window() || w->menu_window() || w->parent()); -} - -- (void)recursivelySendToSubwindows:(SEL)sel applyToSelf:(BOOL)b -{ - if (b) [self performSelector:sel]; - NSEnumerator *enumerator = [[self childWindows] objectEnumerator]; - id child; - while ((child = [enumerator nextObject]) != nil) { - if ([child isKindOfClass:[FLWindow class]]) [child recursivelySendToSubwindows:sel applyToSelf:YES]; - } -} - -- (void)setSubwindowFrame { // have the cocoa position and size of a (sub)window follow its FLTK data - Fl_Window *parent = w->window(); - if (!w->visible_r()) return; - NSPoint pt = FLTKtoCocoa(w, w->x(), w->y(), w->h()); - float s = Fl::screen_driver()->scale(0); - int bt = parent ? 0 : get_window_frame_sizes(w); - NSRect rp = NSMakeRect(round(pt.x), round(pt.y), round(s * w->w()), round(s * w->h()) + bt); - if (!NSEqualRects(rp, [self frame])) { - [self setFrame:rp display:(views_use_CA ? NO : YES)]; - } - if (parent && ![self parentWindow]) { // useful when subwin is first shown, not when moved - FLWindow *pxid = fl_xid(parent); - [pxid addChildWindow:self ordered:NSWindowAbove]; // needs OS X 10.2 - [self orderWindow:NSWindowAbove relativeTo:[pxid windowNumber]]; // necessary under 10.3 - } -} - -- (void)checkSubwindowFrame { - if (!w->parent()) return; - // make sure this subwindow doesn't leak out of its parent window - Fl_Window *from = w, *parent; - CGRect full = CGRectMake(0, 0, w->w(), w->h()); // full subwindow area - CGRect srect = full; // will become new subwindow clip - int fromx = 0, fromy = 0; - while ((parent = from->window()) != NULL) { // loop over all parent windows - fromx -= from->x(); // parent origin in subwindow's coordinates - fromy -= from->y(); - CGRect prect = CGRectMake(fromx, fromy, parent->w(), parent->h()); - srect = CGRectIntersection(prect, srect); // area of subwindow inside its parent - from = parent; - } - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(w); - CGRect *r = d->subRect(); - CGRect current_clip = (r ? *r : full); // current subwindow clip - if (!CGRectEqualToRect(srect, current_clip)) { // if new clip differs from current clip - delete r; - FLWindow *xid = fl_xid(w); - FLView *view = (FLView*)[xid contentView]; - if (CGRectEqualToRect(srect, full)) { - r = NULL; - } else { - r = new CGRect(srect); - if (r->size.width == 0 && r->size.height == 0) r->origin.x = r->origin.y = 0; - } - d->subRect(r); - w->redraw(); - if (fl_mac_os_version < 100900) { - NSInteger parent_num = [fl_xid(w->window()) windowNumber]; - [xid orderWindow:NSWindowBelow relativeTo:parent_num]; - [xid orderWindow:NSWindowAbove relativeTo:parent_num]; - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (!views_use_CA || view->aux_bitmap) -#endif - [view display]; // subwindow needs redrawn - } -} - --(void)waitForExpose -{ - if ([self getFl_Window]->shown()) { - // this makes freshly created windows appear on the screen, if they are not there already - NSModalSession session = [NSApp beginModalSessionForWindow:self]; - [NSApp runModalSession:session]; - [NSApp endModalSession:session]; - } -} - -/* With Mac OS 10.11 the green window button makes window fullscreen (covers system menu bar and dock). - When there are subwindows, they are by default constrained not to cover the menu bar - (this is arguably a Mac OS bug). - Overriding the constrainFrameRect:toScreen: method removes this constraint. - */ -- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen -{ - if ([self parentWindow]) return frameRect; // do not constrain subwindows - return [super constrainFrameRect:frameRect toScreen:screen]; // will prevent a window from going above the menu bar -} -- (NSBitmapImageRep*)rect_to_NSBitmapImageRep:(Fl_Rect*)r { - return rect_to_NSBitmapImageRep(w, r->x(), r->y(), r->w(), r->h()); -} -- (void)makeKeyWindow { - // Necessary in this scenario at least: - // transition of a subwindow-containing window from multiscreen-fullscreen mode to normal mode. - if ([self canBecomeKeyWindow]) [super makeKeyWindow]; -} -@end - -@interface FLApplication : NSObject -{ -} -+ (void)sendEvent:(NSEvent *)theEvent; -@end - -/* - * This function is the central event handler. - * It reads events from the event queue using the given maximum time - */ -static int do_queued_events( double time = 0.0 ) -{ - static int got_events; // not sure the static is necessary here - got_events = 0; - - // Check for re-entrant condition - if ( dataready.IsThreadRunning() ) { - dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); - } - - // Start thread to watch for data ready - if ( dataready.GetNfds() ) { - dataready.StartThread(); - } - - // Elapse timeouts and calculate waiting time - Fl_Timeout::elapse_timeouts(); - time = Fl_Timeout::time_to_wait(time); - - fl_unlock_function(); - NSEvent *event; - while ( (event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate dateWithTimeIntervalSinceNow:time] - inMode:NSDefaultRunLoopMode - dequeue:YES]) != nil ) { - got_events = 1; - [FLApplication sendEvent:event]; // will then call [NSApplication sendevent:] - time = 0; - } - fl_lock_function(); - - return got_events; -} - -double Fl_Darwin_System_Driver::wait(double time_to_wait) -{ - if (dropped_files_list) { // when the list of dropped files is not empty, open one and remove it from list - drain_dropped_files_list(); - } - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - time_to_wait = Fl_System_Driver::wait(time_to_wait); - // the deprecation warnings can be ignored because they run only for macOS < 10.11 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if (fl_mac_os_version < 101100) NSDisableScreenUpdates(); // deprecated 10.11 - Fl::flush(); - if (fl_mac_os_version < 101100) NSEnableScreenUpdates(); // deprecated 10.11 -#pragma clang diagnostic pop - if (Fl::idle()) // 'idle' may have been set within flush() - time_to_wait = 0.0; - int retval = do_queued_events(time_to_wait); - - Fl_Cocoa_Window_Driver::q_release_context(); - [pool release]; - return retval; -} - -static NSInteger max_normal_window_level(void) -{ - Fl_X *x; - NSInteger max_level; - - max_level = 0; - - for (x = Fl_X::first;x;x = x->next) { - NSInteger level; - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (!win || !cw || ![cw isVisible]) - continue; - if (win->modal() || win->non_modal()) - continue; - level = [cw level]; - if (level >= max_level) - max_level = level; - } - - return max_level; -} - -// appropriate window level for modal windows -static NSInteger modal_window_level(void) -{ - NSInteger level; - - level = max_normal_window_level(); - if (level < NSStatusWindowLevel) - return NSStatusWindowLevel; - - // Need some room for non-modal windows - level += 2; - - // We cannot exceed this - if (level > CGShieldingWindowLevel()) - return CGShieldingWindowLevel(); - - return level; -} - -// appropriate window level for non-modal windows -static NSInteger non_modal_window_level(void) -{ - NSInteger level; - - level = max_normal_window_level(); - if (level < NSFloatingWindowLevel) - return NSFloatingWindowLevel; - - level += 1; - - if (level > CGShieldingWindowLevel()) - return CGShieldingWindowLevel(); - - return level; -} - -// makes sure modal and non-modal windows stay on top -static void fixup_window_levels(void) -{ - NSInteger modal_level, non_modal_level; - - Fl_X *x; - FLWindow *prev_modal, *prev_non_modal; - - modal_level = modal_window_level(); - non_modal_level = non_modal_window_level(); - - prev_modal = NULL; - prev_non_modal = NULL; - - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (!win || !cw || ![cw isVisible]) - continue; - if (win->modal()) { - if ([cw level] != modal_level) { - [cw setLevel:modal_level]; - // changing level puts then in front, so make sure the - // stacking isn't messed up - if (prev_modal != NULL) - [cw orderWindow:NSWindowBelow - relativeTo:[prev_modal windowNumber]]; - } - prev_modal = cw; - } else if (win->non_modal()) { - if ([cw level] != non_modal_level) { - [cw setLevel:non_modal_level]; - if (prev_non_modal != NULL) - [cw orderWindow:NSWindowBelow - relativeTo:[prev_non_modal windowNumber]]; - } - prev_non_modal = cw; - } - } -} - - -// updates Fl::e_x, Fl::e_y, Fl::e_x_root, and Fl::e_y_root -static void update_e_xy_and_e_xy_root(NSWindow *nsw) -{ - NSPoint pt; - pt = [nsw mouseLocationOutsideOfEventStream]; - float s = Fl::screen_driver()->scale(0); - Fl::e_x = int(pt.x / s); - Fl::e_y = int(([[nsw contentView] frame].size.height - pt.y)/s); - pt = [NSEvent mouseLocation]; - Fl::e_x_root = int(pt.x/s); - Fl::e_y_root = int((main_screen_height - pt.y)/s); -} - - -/* - * Cocoa Mousewheel handler - */ -static void cocoaMouseWheelHandler(NSEvent *theEvent) -{ - // Handle the new "MightyMouse" mouse wheel events. Please, someone explain - // to me why Apple changed the API on this even though the current API - // supports two wheels just fine. Matthias, - fl_lock_function(); - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - Fl::first_window(window); - // Under OSX, mousewheel deltas are floats, but fltk only supports ints. - float s = Fl::screen_driver()->scale(0); - float edx = [theEvent deltaX]; - float edy = [theEvent deltaY]; - int dx = roundf(edx / s); - int dy = roundf(edy / s); - // make sure that even small wheel movements count at least as one unit - if (edx>0.0f) dx++; else if (edx<0.0f) dx--; - if (edy>0.0f) dy++; else if (edy<0.0f) dy--; - // allow both horizontal and vertical movements to be processed by the widget - if (dx) { - Fl::e_dx = -dx; - Fl::e_dy = 0; - Fl::handle( FL_MOUSEWHEEL, window ); - } - if (dy) { - Fl::e_dx = 0; - Fl::e_dy = -dy; - Fl::handle( FL_MOUSEWHEEL, window ); - } - fl_unlock_function(); -} - -/* - * Cocoa Magnify Gesture Handler - */ -static void cocoaMagnifyHandler(NSEvent *theEvent) -{ - fl_lock_function(); - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - if ( !window->shown() ) { - fl_unlock_function(); - return; - } - Fl::first_window(window); - Fl::e_dy = [theEvent magnification]*1000; // 10.5.2 - if ( Fl::e_dy) { - NSPoint pos = [theEvent locationInWindow]; - pos.y = window->h() - pos.y; - NSUInteger mods = [theEvent modifierFlags]; - mods_to_e_state( mods ); - update_e_xy_and_e_xy_root([theEvent window]); - Fl::handle( FL_ZOOM_GESTURE, window ); - } - fl_unlock_function(); -} - -#if defined(FLTK_HAVE_PEN_SUPPORT) - -static bool cocoaTabletHandler(NSEvent *theEvent, bool lock) -{ - if (lock) fl_lock_function(); - auto theWindow = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - auto ret = fl_cocoa_tablet_handler(theEvent, theWindow); - if (lock) fl_unlock_function(); - return ret; -} - -#endif // FLTK_HAVE_PEN_SUPPORT - -namespace Fl { -namespace Private { -// Global mouse position at mouse down event -int e_x_down { 0 }; -int e_y_down { 0 }; -}; // namespace Private -}; // namespace Fl - -/* - * Cocoa Mouse Button Handler - */ -static void cocoaMouseHandler(NSEvent *theEvent) -{ - static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2, FL_Button+4, FL_Button+5 }; - - fl_lock_function(); - -#if defined(FLTK_HAVE_PEN_SUPPORT) - // Handle tablet proximity and point subevents - if ( ([theEvent type] != NSEventTypeMouseEntered) // does not have a subtype - && ([theEvent type] != NSEventTypeMouseExited) ) // does not have a subtype - { - if ( ([theEvent subtype] == NSEventSubtypeTabletPoint) - || ([theEvent subtype] == NSEventSubtypeTabletProximity) ) - { - if (cocoaTabletHandler(theEvent, false)) { - fl_unlock_function(); - return; - } - // else fall through into mouse event handling - } - } -#endif // FLTK_HAVE_PEN_SUPPORT - - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - if (!window || !window->shown() ) { - fl_unlock_function(); - return; - } - - NSPoint pos = [theEvent locationInWindow]; - float s = Fl::screen_driver()->scale(0); - pos.x /= s; pos.y /= s; - pos.y = window->h() - pos.y; - NSInteger btn = [theEvent buttonNumber] + 1; - NSUInteger mods = [theEvent modifierFlags]; - int sendEvent = 0; - - NSEventType etype = [theEvent type]; - if (etype == NSEventTypeLeftMouseDown || etype == NSEventTypeRightMouseDown || - etype == NSEventTypeOtherMouseDown) { - if (btn == 1) Fl::e_state |= FL_BUTTON1; - else if (btn == 3) Fl::e_state |= FL_BUTTON2; - else if (btn == 2) Fl::e_state |= FL_BUTTON3; - else if (btn == 4) Fl::e_state |= FL_BUTTON4; - else if (btn == 5) Fl::e_state |= FL_BUTTON5; - } - else if (etype == NSEventTypeLeftMouseUp || etype == NSEventTypeRightMouseUp || - etype == NSEventTypeOtherMouseUp) { - if (btn == 1) Fl::e_state &= ~FL_BUTTON1; - else if (btn == 3) Fl::e_state &= ~FL_BUTTON2; - else if (btn == 2) Fl::e_state &= ~FL_BUTTON3; - else if (btn == 4) Fl::e_state &= ~FL_BUTTON4; - else if (btn == 5) Fl::e_state &= ~FL_BUTTON5; - } - - switch ( etype ) { - case NSEventTypeLeftMouseDown: - case NSEventTypeRightMouseDown: - case NSEventTypeOtherMouseDown: - sendEvent = FL_PUSH; - Fl::e_is_click = 1; - Fl::Private::e_x_down = (int)pos.x; - Fl::Private::e_y_down = (int)pos.y; - if ([theEvent clickCount] > 1) - Fl::e_clicks++; - else - Fl::e_clicks = 0; - // fall through - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseUp: - if ( !window ) break; - if ( !sendEvent ) { - sendEvent = FL_RELEASE; - } - Fl::e_keysym = keysym[ btn ]; - // fall through - case NSEventTypeMouseMoved: - if ( !sendEvent ) { - sendEvent = FL_MOVE; - } - // fall through - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: { - if ( !sendEvent ) { - sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG - if ( (fabs(pos.x - Fl::Private::e_x_down) > 5) || - (fabs(pos.y - Fl::Private::e_y_down) > 5)) - Fl::e_is_click = 0; - } - mods_to_e_state( mods ); - update_e_xy_and_e_xy_root([theEvent window]); - Fl::handle( sendEvent, window ); - } - break; - case NSEventTypeMouseEntered : - if ([theEvent window]) update_e_xy_and_e_xy_root([theEvent window]); - Fl::handle(FL_ENTER, window); - break; - case NSEventTypeMouseExited : - Fl::handle(FL_LEAVE, window); - break; - default: - break; - } - - fl_unlock_function(); - - return; -} - - -@interface FLTextView : NSTextView // this subclass is only needed under OS X < 10.6 -{ - BOOL isActive; -} -+ (void)initialize; -+ (FLTextView*)singleInstance; -- (void)insertText:(id)aString; -- (void)doCommandBySelector:(SEL)aSelector; -- (void)setActive:(BOOL)a; -@end -static FLTextView *fltextview_instance = nil; -@implementation FLTextView -+ (void)initialize { - NSRect rect={{0,0},{20,20}}; - fltextview_instance = [[FLTextView alloc] initWithFrame:rect]; -} -+ (FLTextView*)singleInstance { - return fltextview_instance; -} -- (void)insertText:(id)aString -{ - if (isActive) [[[NSApp keyWindow] contentView] insertText:aString]; -} -- (void)doCommandBySelector:(SEL)aSelector -{ - [[[NSApp keyWindow] contentView] doCommandBySelector:aSelector]; -} -- (void)setActive:(BOOL)a -{ - isActive = a; -} -@end - - -@interface FLWindowDelegate : NSObject -+ (void)initialize; -+ (FLWindowDelegate*)singleInstance; -- (void)windowDidMove:(NSNotification *)notif; -- (void)view_did_resize:(NSNotification *)notif; -- (void)windowDidResignKey:(NSNotification *)notif; -- (void)windowDidBecomeKey:(NSNotification *)notif; -- (void)windowDidBecomeMain:(NSNotification *)notif; -- (void)windowDidDeminiaturize:(NSNotification *)notif; -- (void)fl_windowMiniaturize:(NSNotification *)notif; -- (void)windowDidMiniaturize:(NSNotification *)notif; -- (void)windowWillEnterFullScreen:(NSNotification *)notif; -- (void)windowWillExitFullScreen:(NSNotification *)notif; -- (BOOL)windowShouldClose:(id)fl; -- (void)anyWindowWillClose:(NSNotification *)notif; -- (void)doNothing:(id)unused; -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; -@end - - -/* make subwindows re-appear after appl unhide or window deminiaturize - (not necessary with 10.5 and above) - */ -static void orderfront_subwindows(FLWindow *xid) -{ - NSArray *children = [xid childWindows]; // 10.2 - NSEnumerator *enumerator = [children objectEnumerator]; - id child; - while ((child = [enumerator nextObject]) != nil) { // this undo-redo seems necessary under 10.3 - [xid removeChildWindow:child]; - [xid addChildWindow:child ordered:NSWindowAbove]; - [child orderWindow:NSWindowAbove relativeTo:[xid windowNumber]]; - orderfront_subwindows(child); - } -} - - -// compute coordinates of the win top left in FLTK units -static void CocoatoFLTK(Fl_Window *win, int &x, int &y) { - NSPoint ori; - FLWindow *nsw = fl_xid(win); - ori = [nsw convertBaseToScreen:NSMakePoint(0, [[nsw contentView] frame].size.height)]; - float s = Fl::screen_driver()->scale(0); - x = (int)lround(ori.x / s); - y = (int)lround((main_screen_height - ori.y) / s); - while (win->parent()) {win = win->window(); x -= win->x(); y -= win->y();} -} - -// return Cocoa coordinates of the point in window win at (x,y) FLTK units -static NSPoint FLTKtoCocoa(Fl_Window *win, int x, int y, int H) { - float s = Fl::screen_driver()->scale(0); - while (win->parent()) {win = win->window(); x += win->x(); y += win->y();} - return NSMakePoint(round(x * s), main_screen_height - round((y + H)*s)); -} - -static FLWindowDelegate *flwindowdelegate_instance = nil; -@implementation FLWindowDelegate -+ (void)initialize -{ - if (self == [FLWindowDelegate self]) { - flwindowdelegate_instance = [FLWindowDelegate alloc]; - flwindowdelegate_instance = [flwindowdelegate_instance init]; - } -} -+ (FLWindowDelegate*)singleInstance { - return flwindowdelegate_instance; -} -- (void)windowDidMove:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - if (!window->parent()) starting_moved_window = window; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - FLView *view = (FLView*)[nsw contentView]; - if (views_use_CA && [view did_view_resolution_change]) { - if (window->as_gl_window() && Fl::use_high_res_GL()) [view setNeedsDisplay:YES]; // necessary with macOS ≥ 10.14.2; harmless before - } -#endif - if (window == starting_moved_window) { - // we update 'main_screen_height' here because it's wrong just after screen config changes - main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; - int X, Y; - CocoatoFLTK(window, X, Y); - if (window->x() != X || window->y() != Y) { - if (!Fl_Cocoa_Window_Driver::driver(window)->through_resize()) - window->position(X, Y); - else - window->Fl_Widget::resize(X,Y,window->w(),window->h()); - } - update_e_xy_and_e_xy_root(nsw); - // at least since MacOS 10.9: OS moves subwindows contained in a moved window - // setSubwindowFrame is no longer necessary. - if (fl_mac_os_version < 100900) [nsw recursivelySendToSubwindows:@selector(setSubwindowFrame) applyToSelf:NO]; - if (window->parent()) [nsw recursivelySendToSubwindows:@selector(checkSubwindowFrame) applyToSelf:YES]; - starting_moved_window = NULL; - } - if (!window->parent()) { - int nscreen = Fl::screen_num(window->x(), window->y(), window->w(), window->h()); - Fl_Window_Driver::driver(window)->screen_num(nscreen); - } - fl_unlock_function(); -} - -/* - This method is called whenever the view of an Fl_Window changes size. - - This can happen for various reasons: - - - the user resizes a desktop window (NSViewFrameDidChangeNotification) - Fl_Cocoa_Window_Driver::driver(window)->through_resize() == 0 for the top level window - Fl_Window::is_a_rescale() == 0 - - the app scale is changed (the Cocoa size changes, but the FLTK size remains) - Fl_Cocoa_Window_Driver::driver(window)->through_resize() == 1 - Fl_Window::is_a_rescale() == 1 - - a window is resized by application code: Fl_Window:resize() - Fl_Cocoa_Window_Driver::driver(window)->through_resize() == 1 - Fl_Window::is_a_rescale() == 0 - - Note that a top level window must be treated differently than a subwindow - (an Fl_Window that is the child of another window). - - Also note, it's important to keep the logical FLTK coordinate system intact. - Converting Cocoa coordinates into FLTK coordinates is not reliable because - it loses precision if the screen scale is set to anything but 1:1. - - See also: - Fl_Cocoa_Window_Driver::driver(window)->view_resized() avoid recursion - Fl_Cocoa_Window_Driver::driver(window)->through_resize(); avoid recursion - Fl_Cocoa_Window_Driver::driver(window)->changed_resolution(); tested OK - */ -- (void)view_did_resize:(NSNotification *)notif -{ - if (![[notif object] isKindOfClass:[FLView class]]) return; - FLView *view = (FLView*)[notif object]; - FLWindow *nsw = (FLWindow*)[view window]; - if (!nsw || ![nsw getFl_Window]) return; - fl_lock_function(); - Fl_Window *window = [nsw getFl_Window]; - - int X, Y, W, H; - float s = Fl::screen_driver()->scale(window->screen_num()); - if (Fl_Window::is_a_rescale()) { - if (window->parent()) { - X = window->x(); - Y = window->y(); - } else { - // Recalculate the FLTK position from the current Cocoa position applying - // the new scale, so the window stays at its current position after scaling. - CocoatoFLTK(window, X, Y); - } - W = window->w(); - H = window->h(); - } else if (Fl_Cocoa_Window_Driver::driver(window)->through_resize()) { - if (window->parent()) { - X = window->x(); - Y = window->y(); - } else { - // Recalculate the FLTK position from the current Cocoa position - CocoatoFLTK(window, X, Y); - } - W = window->w(); - H = window->h(); - } else { - CocoatoFLTK(window, X, Y); - NSRect r = [view frame]; - W = (int)lround(r.size.width/s); - H = (int)lround(r.size.height/s); - } - - Fl_Cocoa_Window_Driver::driver(window)->view_resized(1); - if (Fl_Cocoa_Window_Driver::driver(window)->through_resize()) { - if (window->as_gl_window()) { - static Fl_Cocoa_Plugin *plugin = NULL; - if (!plugin) { - Fl_Plugin_Manager pm("fltk:cocoa"); - plugin = (Fl_Cocoa_Plugin*)pm.plugin("gl.cocoa.fltk.org"); - } - // calls Fl_Gl_Window::resize() without including Fl_Gl_Window.H - plugin->resize(window->as_gl_window(), X, Y, W, H); - } else { - Fl_Cocoa_Window_Driver::driver(window)->resize(X, Y, W, H); - } - } else - window->resize(X, Y, W, H); - Fl_Cocoa_Window_Driver::driver(window)->view_resized(0); - update_e_xy_and_e_xy_root(nsw); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (views_use_CA && !window->as_gl_window()) { - [view reset_aux_bitmap]; - window->redraw(); - } -#endif - if (!window->parent() && window->border() && Fl_Window_Driver::driver(window)->is_resizable()) { - Fl_Cocoa_Window_Driver::driver(window)->is_maximized([nsw isZoomed] && - !window->fullscreen_active()); - } - fl_unlock_function(); -} -- (void)windowDidResignKey:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - /* Fullscreen windows obscure all other windows so we need to return - to a "normal" level when the user switches to another window or another app */ - if (window->fullscreen_active()) { - [nsw setLevel:NSNormalWindowLevel]; - fixup_window_levels(); - } - // Situations such as opening a character palette produce windowDidResignKey but - // [NSApp keyWindow] remains set to the resigning window. In that case, don't send FL_UNFOCUS - if ([NSApp keyWindow] != nsw) Fl::handle(FL_UNFOCUS, window); - fl_unlock_function(); -} -- (void)windowDidBecomeKey:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *w = [nsw getFl_Window]; - /* Restore previous fullscreen level */ - if (w->fullscreen_active() && !(nsw.styleMask & NSWindowStyleMaskFullScreen)) { - [nsw setLevel:NSStatusWindowLevel]; - fixup_window_levels(); - } - Fl::handle( FL_FOCUS, w); - fl_unlock_function(); -} -- (void)windowDidBecomeMain:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - Fl::first_window(window); - if (!window->parent()) [nsw orderFront:nil]; - if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style()) { - // select the corresponding Window menu item - int index = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->first_window_menu_item; - while (index > 0) { - Fl_Menu_Item *item = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->window_menu_items + index; - if (!item->label()) break; - if (item->user_data() == window) { - if (!item->value()) { - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->setonly(item); - } - break; - } - index++; - } - } - fl_unlock_function(); -} -- (void)windowDidDeminiaturize:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - if ([nsw miniwindowImage]) { [nsw setMiniwindowImage:nil]; } - Fl_Window *window = [nsw getFl_Window]; - Fl::handle(FL_SHOW, window); - // necessary when resolutions before miniaturization and after deminiaturization differ - // or if GUI was resized while window was minimized - [nsw recursivelySendToSubwindows:@selector(setSubwindowFrame) applyToSelf:YES]; - update_e_xy_and_e_xy_root(nsw); - Fl::flush(); // Process redraws set by FL_SHOW. - fl_unlock_function(); -} -- (void)fl_windowMiniaturize:(NSNotification *)notif -{ - // subwindows are not captured in system-built miniature window image - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - if ([[nsw childWindows] count]) { - Fl_Window *window = [nsw getFl_Window]; - // capture the window and its subwindows and use as miniature window image - NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep_subwins(window, 0, 0, window->w(), window->h(), true); - if (bitmap) { - NSImage *img = [[[NSImage alloc] initWithSize:NSMakeSize([bitmap pixelsWide], [bitmap pixelsHigh])] autorelease]; - [img addRepresentation:bitmap]; - [bitmap release]; - [nsw setMiniwindowImage:img]; - } - } - fl_unlock_function(); -} -- (void)windowDidMiniaturize:(NSNotification *)notif -{ - [self fl_windowMiniaturize:notif]; - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - Fl::handle(FL_HIDE, window); - fl_unlock_function(); -} -- (void)windowWillEnterFullScreen:(NSNotification *)notif -{ - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - window->_set_fullscreen(); -} -- (void)windowWillExitFullScreen:(NSNotification *)notif -{ - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - window->_clear_fullscreen(); -} -- (BOOL)windowShouldClose:(id)fl -{ - fl_lock_function(); - Fl_Window *win = [(FLWindow *)fl getFl_Window]; - if (win) Fl::handle(FL_CLOSE, win); // this might or might not close the window - fl_unlock_function(); - // the system doesn't need to send [fl close] because FLTK does it when needed - return NO; -} -- (void)anyWindowWillClose:(NSNotification *)notif -{ - fl_lock_function(); - if ([[notif object] isKeyWindow]) { - // If the closing window is the key window, - // find a bordered top-level window to become the new key window - Fl_Window *w = Fl::first_window(); - while (w && (w->parent() || !w->border() || !w->visible())) { - w = Fl::next_window(w); - } - if (w) { - [fl_mac_xid(w) makeKeyWindow]; - } - } - fl_unlock_function(); -} -- (void)doNothing:(id)unused -{ - return; -} -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu { - return NO; -} -@end - -@interface FLAppDelegate : NSObject -{ - @public - open_cb_f_type open_cb; - TSMDocumentID currentDoc; -} -- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; -- (void)applicationDidFinishLaunching:(NSNotification *)notification; -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender; -- (void)applicationDidBecomeActive:(NSNotification *)notify; -- (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification; -- (void)applicationDidUpdate:(NSNotification *)aNotification; -- (void)applicationWillResignActive:(NSNotification *)notify; -- (void)applicationWillHide:(NSNotification *)notify; -- (void)applicationWillUnhide:(NSNotification *)notify; -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; -@end - -@implementation FLAppDelegate -- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { - // Avoids macOS 14 warning message when app is launched from command line: - // "WARNING: Secure coding is automatically enabled for restorable state! - // However, not on all supported macOS versions of this application. - // Opt-in to secure coding explicitly by implementing - // NSApplicationDelegate.applicationSupportsSecureRestorableState:." - return (fl_mac_os_version >= 140000); -} -- (void)applicationDidFinishLaunching:(NSNotification *)notification -{ - if (fl_mac_os_version >= 101300 && [NSApp isRunning]) [NSApp stop:nil]; -} -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender -{ - fl_lock_function(); - while ( Fl_X::first ) { - Fl_Window *win = Fl::first_window(); - if (win->parent()) win = win->top_window(); - Fl_Widget_Tracker wt(win); // track the window object - Fl::handle(FL_CLOSE, win); - if (wt.exists() && win->shown()) { // the user didn't close win - break; - } - } - fl_unlock_function(); - if ( ! Fl::first_window() ) { - Fl::program_should_quit(1); - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); // necessary when called through menu and in Fl::wait() - } - return NSTerminateCancel; -} -- (void)applicationDidBecomeActive:(NSNotification *)notify -{ - fl_lock_function(); - - // update clipboard status - clipboard_check(); - - /** - * Cocoa organizes the Z depth of windows on a global priority. FLTK however - * expects the window manager to organize Z level by application. The trickery - * below will change Z order during activation and deactivation. - */ - fixup_window_levels(); - - Fl::handle(FL_APP_ACTIVATE, nullptr); - fl_unlock_function(); -} -- (void)applicationDidChangeScreenParameters:(NSNotification *)unused -{ // react to changes in screen numbers and positions - fl_lock_function(); - main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; - Fl::call_screen_init(); - Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); - fl_unlock_function(); -} -- (void)applicationDidUpdate:(NSNotification *)aNotification -{ - if (im_enabled != -1) { - TSMDocumentID newDoc; - // It is extremely unclear when Cocoa decides to create/update - // the input context, but debugging reveals that it is done - // by NSApplication:updateWindows. So check if the input context - // has shifted after each such run so that we can update our - // input methods status. - newDoc = TSMGetActiveDocument(); - if (newDoc != currentDoc) { - TSMDocumentID doc; - - doc = TSMGetActiveDocument(); - - if (im_enabled) - TSMRemoveDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag); - else { - CFArrayRef inputSources; - CFDictionaryRef filter; - // FLΤΚ previously used TISCreateASCIICapableInputSourceList(), - // which mostly hits the mark. But it excludes things like Greek - // and Cyrillic keyboards. So let's be more explicit. - filter = CFDictionaryCreate(NULL, (const void **)kTISPropertyInputSourceType, - (const void **)kTISTypeKeyboardLayout, - 1, NULL, NULL); - inputSources = TISCreateInputSourceList(filter, false); - CFRelease(filter); - TSMSetDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag, - sizeof(CFArrayRef), &inputSources); - CFRelease(inputSources); - } - currentDoc = newDoc; - } - } -} -- (void)applicationWillResignActive:(NSNotification *)notify -{ - fl_lock_function(); - Fl_X *x; - FLWindow *top = 0; - // sort in all regular windows - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (win && cw) { - if (win->modal()) { - } else if (win->non_modal()) { - } else { - if (!top) top = cw; - } - } - } - // now sort in all modals - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (win && cw && [cw isVisible]) { - if (win->modal()) { - [cw setLevel:NSNormalWindowLevel]; - if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]]; - } - } - } - // finally all non-modals - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (win && cw && [cw isVisible]) { - if (win->non_modal()) { - [cw setLevel:NSNormalWindowLevel]; - if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]]; - } - } - } - Fl::handle(FL_APP_DEACTIVATE, nullptr); - fl_unlock_function(); -} -- (void)applicationWillHide:(NSNotification *)notify -{ - fl_lock_function(); - Fl_X *x; - for (x = Fl_X::first;x;x = x->next) { - Fl_Window *window = x->w; - if ( !window->parent() ) Fl::handle( FL_HIDE, window); - } - fl_unlock_function(); -} -- (void)applicationWillUnhide:(NSNotification *)notify -{ - fl_lock_function(); - for (Fl_X *x = Fl_X::first;x;x = x->next) { - Fl_Window *w = x->w; - if ( !w->parent() && ![(FLWindow*)x->xid isMiniaturized]) { - Fl::handle(FL_SHOW, w); - } - } - fl_unlock_function(); -} -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename -{ - if (fl_mac_os_version < 101300) { - // without the next two statements, the opening of the 1st window is delayed by several seconds - // under 10.8 ≤ Mac OS < 10.13 when a file is dragged on the application icon - Fl_Window *firstw = Fl::first_window(); - if (firstw) firstw->wait_for_expose(); - } else if (in_nsapp_run) { // memorize all dropped filenames - if (!dropped_files_list) dropped_files_list = [[NSMutableArray alloc] initWithCapacity:1]; - [dropped_files_list addObject:filename]; - return YES; - } - if (open_cb) { - fl_lock_function(); - (*open_cb)([filename UTF8String]); - Fl::flush(); // useful for AppleScript that does not break the event loop - fl_unlock_function(); - return YES; - } - return NO; -} -@end - - -static void drain_dropped_files_list() { - open_cb_f_type open_cb = ((FLAppDelegate*)[NSApp delegate])->open_cb; - if (!open_cb) { - [dropped_files_list removeAllObjects]; - [dropped_files_list release]; - dropped_files_list = nil; - return; - } - NSString *s = (NSString*)[dropped_files_list objectAtIndex:0]; - char *fname = fl_strdup([s UTF8String]); - [dropped_files_list removeObjectAtIndex:0]; - if ([dropped_files_list count] == 0) { - [dropped_files_list release]; - dropped_files_list = nil; - } - open_cb(fname); - free(fname); -} - -/* - * Install an open documents event handler... - */ -void Fl_Darwin_System_Driver::open_callback(void (*cb)(const char *)) { - fl_open_display(); - ((FLAppDelegate*)[NSApp delegate])->open_cb = cb; -} - -@implementation FLApplication -+ (void)sendEvent:(NSEvent *)theEvent -{ - if (fl_send_system_handlers(theEvent)) - return; - - NSEventType type = [theEvent type]; - if (type == NSEventTypeLeftMouseDown) { - fl_lock_function(); - Fl_Window *grab = Fl::grab(); - if (grab) { - FLWindow *win = (FLWindow *)[theEvent window]; - if ( [win isKindOfClass:[FLWindow class]] && grab != [win getFl_Window]) { - // a click event out of a menu window, so we should close this menu - // done here to catch also clicks on window title bar/resize box - cocoaMouseHandler(theEvent); - } - } - fl_unlock_function(); - } else if (type == NSEventTypeApplicationDefined) { - if ([theEvent subtype] == FLTKDataReadyEvent) { - processFLTKEvent(); - } - return; - } else if (type == NSEventTypeKeyUp) { - // The default sendEvent turns key downs into performKeyEquivalent when - // modifiers are down, but swallows the key up if the modifiers include - // command. This one makes all modifiers consistent by always sending key ups. - // FLView treats performKeyEquivalent to keyDown, but performKeyEquivalent is - // still needed for the system menu. - [[NSApp keyWindow] sendEvent:theEvent]; - return; - } - [NSApp sendEvent:theEvent]; -} -@end - -/* Prototype of undocumented function needed to support Mac OS 10.2 or earlier - extern "C" { - OSErr CPSEnableForegroundOperation(ProcessSerialNumber*, UInt32, UInt32, UInt32, UInt32); -} -*/ - -static BOOL is_bundled() { - static int value = 2; - if (value == 2) { - value = 1; - NSBundle *bundle = [NSBundle mainBundle]; - if (bundle) { - NSString *exe = [[bundle executablePath] stringByStandardizingPath]; - NSString *bpath = [[bundle bundlePath] stringByStandardizingPath]; - NSString *exe_dir = [exe stringByDeletingLastPathComponent]; -//NSLog(@"exe=%@ bpath=%@ exe_dir=%@",exe, bpath, exe_dir); - if ([bpath isEqualToString:exe] || [bpath isEqualToString:exe_dir]) value = 0; - } else value = 0; - } - return value == 1; -} - - -static void foreground_and_activate() { - if ( !is_bundled() ) { // only transform the application type for unbundled apps - ProcessSerialNumber cur_psn = { 0, kCurrentProcess }; - TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); // needs Mac OS 10.3 - /* support of Mac OS 10.2 or earlier used this undocumented call instead - err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103); - */ - } - [NSApp activateIgnoringOtherApps:YES]; -} - -// simpler way to activate application tested OK on MacOS 10.3 10.6 10.9 10.13 and 10.14 public beta - -void Fl_Cocoa_Screen_Driver::open_display_platform() { - static char beenHereDoneThat = 0; - if ( !beenHereDoneThat ) { - beenHereDoneThat = 1; - - BOOL need_new_nsapp = (NSApp == nil); - if (need_new_nsapp) [NSApplication sharedApplication]; - NSAutoreleasePool *localPool; - localPool = [[NSAutoreleasePool alloc] init]; // never released - FLAppDelegate *delegate = [FLAppDelegate alloc]; - [(NSApplication*)NSApp setDelegate:[delegate init]]; - if (need_new_nsapp) { - if (fl_mac_os_version >= 101300 && fl_mac_os_version < 140000 && is_bundled()) { - [NSApp activateIgnoringOtherApps:YES]; - in_nsapp_run = true; - [NSApp run]; - in_nsapp_run = false; - } - else { - [NSApp finishLaunching]; - // Unbundled app may require this so delegate receives applicationDidFinishLaunching: - // even if doc states this is sent at the end of finishLaunching. - if (!is_bundled()) [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:nil - inMode:NSDefaultRunLoopMode - dequeue:NO]; - } - } - if (fl_mac_os_version < 140000) { - // empty the event queue but keep system events for drag&drop of files at launch - NSEvent *ign_event; - do ign_event = [NSApp nextEventMatchingMask:(NSEventMaskAny & ~NSEventMaskSystemDefined) - untilDate:[NSDate dateWithTimeIntervalSinceNow:0] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - while (ign_event); - } - if (![NSApp isActive]) foreground_and_activate(); - if (![NSApp servicesMenu]) createAppleMenu(); - else Fl_Sys_Menu_Bar::window_menu_style(Fl_Sys_Menu_Bar::no_window_menu); - main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; - [[NSNotificationCenter defaultCenter] addObserver:[FLWindowDelegate singleInstance] - selector:@selector(anyWindowWillClose:) - name:NSWindowWillCloseNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:[FLWindowDelegate singleInstance] - selector:@selector(view_did_resize:) - name:NSViewFrameDidChangeNotification - object:nil]; - if (![NSThread isMultiThreaded]) { - // With old OS X versions, it is necessary to create one thread for secondary pthreads to be - // allowed to use cocoa, especially to create an NSAutoreleasePool. - // We create a thread that does nothing so it completes very fast: - [NSThread detachNewThreadSelector:@selector(doNothing:) toTarget:[FLWindowDelegate singleInstance] withObject:nil]; - } - (void)localPool; // silence warning - } -} - - -// Force a "Roman" or "ASCII" keyboard, which both the Mozilla and -// Safari people seem to think implies turning off advanced IME stuff -// (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput -// in Safari/Webcore). Should be good enough for us then... - -static int input_method_startup() -{ - static int retval = -1; // -1: not initialized, 0: not usable, 1: ready for use - if (retval == -1) { - fl_open_display(); - // These symbols require 10.5. They are no longer visible in Apple doc. - // They do exist in Carbon.framework --> HIToolbox.framework --> TextServices.h - TSMGetActiveDocument = (TSMGetActiveDocument_type)Fl_Darwin_System_Driver::get_carbon_function("TSMGetActiveDocument"); - TSMSetDocumentProperty = (TSMSetDocumentProperty_type)Fl_Darwin_System_Driver::get_carbon_function("TSMSetDocumentProperty"); - TSMRemoveDocumentProperty = (TSMRemoveDocumentProperty_type)Fl_Darwin_System_Driver::get_carbon_function("TSMRemoveDocumentProperty"); - // These symbols are no longer visible in Apple doc. - // They do exist in Carbon.framework --> HIToolbox.framework --> TextInputSources.h - TISCreateInputSourceList = (TISCreateInputSourceList_type)Fl_Darwin_System_Driver::get_carbon_function("TISCreateInputSourceList"); - kTISTypeKeyboardLayout = (CFStringRef)Fl_Darwin_System_Driver::get_carbon_function("kTISTypeKeyboardLayout"); - kTISPropertyInputSourceType = (CFStringRef)Fl_Darwin_System_Driver::get_carbon_function("kTISPropertyInputSourceType"); - retval = (TSMGetActiveDocument && TSMSetDocumentProperty && TSMRemoveDocumentProperty && TISCreateInputSourceList && kTISTypeKeyboardLayout && kTISPropertyInputSourceType ? 1 : 0); - } - return retval; -} - -void Fl_Cocoa_Screen_Driver::enable_im() { - if (!input_method_startup()) return; - - im_enabled = 1; - - ((FLAppDelegate*)[NSApp delegate])->currentDoc = NULL; - [NSApp updateWindows]; // triggers [FLAppDelegate applicationDidUpdate] -} - -void Fl_Cocoa_Screen_Driver::disable_im() { - if (!input_method_startup()) return; - - im_enabled = 0; - - ((FLAppDelegate*)[NSApp delegate])->currentDoc = NULL; - [NSApp updateWindows]; // triggers [FLAppDelegate applicationDidUpdate] -} - - -// Gets the border sizes and the titlebar height -static int get_window_frame_sizes(Fl_Window *win, int *pbx, int *pby) { - if (pbx) *pbx = 0; if (pby) *pby = 0; - if (win && !win->border()) return 0; - FLWindow *flw = fl_xid(win); - if (flw) { - return [flw frame].size.height - [[flw contentView] frame].size.height; - } - static int top = 0, left, bottom; - if (!top) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSRect inside = { {20,20}, {100,100} }; - NSRect outside = [NSWindow frameRectForContentRect:inside - styleMask:NSWindowStyleMaskTitled]; - left = int(outside.origin.x - inside.origin.x); - bottom = int(outside.origin.y - inside.origin.y); - top = int(outside.size.height - inside.size.height) - bottom; - [pool release]; - } - if (pbx) *pbx = left; - if (pby) *pby = bottom; - return top; -} - -void Fl_Cocoa_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom) { - *top = get_window_frame_sizes(pWindow, left, bottom); - *right = *left; -} - -/* - * smallest x coordinate in screen space of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::x() { - open_display(); - return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].origin.x) / scale(0); -} - - -/* - * smallest y coordinate in screen space of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::y() { - open_display(); - NSRect visible = [[[NSScreen screens] objectAtIndex:0] visibleFrame]; - return int(main_screen_height - (visible.origin.y + visible.size.height)) / scale(0); -} - - -/* - * width of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::w() { - open_display(); - return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].size.width) / scale(0); -} - - -/* - * height of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::h() { - open_display(); - return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].size.height) / scale(0); -} - -// computes the work area of the nth screen (screen #0 has the menubar) -void Fl_Cocoa_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - if (n < 0 || n >= num_screens) n = 0; - open_display(); - NSRect r = [[[NSScreen screens] objectAtIndex:n] visibleFrame]; - X = int(r.origin.x) / scale(0); - Y = (main_screen_height - int(r.origin.y + r.size.height)) / scale(0); - W = int(r.size.width) / scale(0); - H = int(r.size.height) / scale(0); -} - -/* - * get the current mouse pointer world coordinates - */ -int Fl_Cocoa_Screen_Driver::get_mouse(int &x, int &y) -{ - open_display(); - NSPoint pt = [NSEvent mouseLocation]; - x = int(pt.x); - y = int(main_screen_height - pt.y); - return screen_num(x/scale(0), y/scale(0)); -} - - -static int fake_X_wm(Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) { - int W, H, xoff, yoff, dx, dy; - int ret = bx = by = bt = 0; - if (w->border() && !w->parent()) { - int minw, minh, maxw, maxh; - w->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (maxw != minw || maxh != minh) { - ret = 2; - } else { - ret = 1; - } - bt = get_window_frame_sizes(w, &bx, &by); - } - if (w->parent()) return 0; - // The coordinates of the whole window, including non-client area - xoff = bx; - yoff = by + bt; - dx = 2*bx; - dy = 2*by + bt; - float s = Fl::screen_driver()->scale(0); - X = round(w->x()*s)-xoff; - Y = round(w->y()*s)-yoff; - W = w->w()*s+dx; - H = w->h()*s+dy; - - // Proceed to positioning the window fully inside the screen, if possible - - // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk - // that we want to avoid when positioning our window, namely the Dock and the - // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the - // list of all available screens and find the one that this window is most - // likely to go to, and then reposition it to fit withing the 'good' area. - // Rect r; - // find the screen, that the center of this window will fall into - int R = X+W, B = Y+H; // right and bottom - int cx = (X+R)/2, cy = (Y+B)/2; // center of window; - NSScreen *gd = NULL; - NSArray *a = [NSScreen screens]; int count = (int)[a count]; NSRect r; int i; - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( cx >= r.origin.x && cx <= r.origin.x + r.size.width - && cy >= r.origin.y && cy <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - - // if the center doesn't fall on a screen, try the top left - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( X >= r.origin.x && X <= r.origin.x + r.size.width - && Y >= r.origin.y && Y <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // if that doesn't fall on a screen, try the top right - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( R >= r.origin.x && R <= r.origin.x + r.size.width - && Y >= r.origin.y && Y <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // if that doesn't fall on a screen, try the bottom left - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( X >= r.origin.x && X <= r.origin.x + r.size.width - && Y+H >= r.origin.y && Y+H <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // last resort, try the bottom right - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( R >= r.origin.x && R <= r.origin.x + r.size.width - && Y+H >= r.origin.y && Y+H <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // if we still have not found a screen, we will use the main - // screen, the one that has the application menu bar. - if (!gd) gd = [a objectAtIndex:0]; - if (gd) { - r = [gd visibleFrame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( R > r.origin.x + r.size.width ) X -= int(R - (r.origin.x + r.size.width)); - if ( B > r.size.height + r.origin.y ) Y -= int(B - (r.size.height + r.origin.y)); - if ( X < r.origin.x ) X = int(r.origin.x); - if ( Y < r.origin.y ) Y = int(r.origin.y); - } - - // Return the client area's top left corner in (X,Y) - X+=xoff; - Y+=yoff; - X /= s; - Y /= s; - - return ret; -} - - -Fl_Window *fl_dnd_target_window = 0; - -static void q_set_window_title(NSWindow *nsw, const char * name, const char *mininame) { - CFStringRef title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8); - if(!title) { // fallback when name contains malformed UTF-8 - int l = (int)strlen(name); - unsigned short* utf16 = new unsigned short[l + 1]; - l = fl_utf8toUtf16(name, l, utf16, l + 1); - title = CFStringCreateWithCharacters(NULL, utf16, l); - delete[] utf16; - } - [nsw setTitle:(NSString*)title]; - CFRelease(title); - if (mininame && strlen(mininame)) { - CFStringRef minititle = CFStringCreateWithCString(NULL, mininame, kCFStringEncodingUTF8); - if (minititle) { - [nsw setMiniwindowTitle:(NSString*)minititle]; - CFRelease(minititle); - } - } -} - -/** How FLTK handles Mac OS text input - - Let myview be the instance of the FLView class that has the keyboard focus. FLView is an FLTK-defined NSView subclass - that implements the NSTextInputClient protocol to properly handle text input. It also implements the old NSTextInput - protocol to run with OS <= 10.4. The few NSTextInput protocol methods that differ in signature from the NSTextInputClient - protocol transmit the received message to the corresponding NSTextInputClient method. - - Keyboard input sends keyDown: and performKeyEquivalent: messages to myview. The latter occurs for keys such as - ForwardDelete, arrows and F1, and when the Ctrl or Cmd modifiers are used. Other key presses send keyDown: messages. - The keyDown: method calls [myview process_keydown:theEvent] that is equivalent to - [[myview inputContext] handleEvent:theEvent], and triggers system processing of keyboard events. - The performKeyEquivalent: method directly calls Fl::handle(FL_KEYBOARD, focus-window) - when the Ctrl or Cmd modifiers are used. If not, it also calls [[myview inputContext] handleEvent:theEvent]. - The performKeyEquivalent: method returns YES when the keystroke has been handled and NO otherwise, which allows - shortcuts of the system menu to be processed. Three sorts of messages are then sent back by the system to myview: - doCommandBySelector:, setMarkedText: and insertText:. All 3 messages eventually produce Fl::handle(FL_KEYBOARD, win) calls. - The doCommandBySelector: message allows to process events such as new-line, forward and backward delete, arrows, - escape, tab, F1. The message setMarkedText: is sent when marked text, that is, temporary text that gets replaced later - by some other text, is inserted. This happens when a dead key is pressed, and also - when entering complex scripts (e.g., Chinese). Fl_Cocoa_Screen_Driver::next_marked_length gives the byte - length of marked text before the FL_KEYBOARD event is processed. Fl::compose_state gives this length after this processing. - Message insertText: is sent to enter text in the focused widget. If there's marked text, Fl::compose_state is > 0, and this - marked text gets replaced by the inserted text. If there's no marked text, the new text is inserted at the insertion point. - When the character palette is used to enter text, the system sends an insertText: message to myview. - The in_key_event field of the FLView class allows to differentiate keyboard from palette inputs. - - During processing of the handleEvent message, inserted and marked strings are concatenated in a single string - inserted in a single FL_KEYBOARD event after return from handleEvent. The need_handle member variable of FLView allows - to determine when setMarkedText or insertText strings have been sent during handleEvent processing and must trigger - an FL_KEYBOARD event. Concatenating two insertText operations or an insertText followed by a setMarkedText is possible. - In contrast, setMarkedText followed by insertText or by another setMarkedText isn't correct if concatenated in a single - string. Thus, in such case, the setMarkedText and the next operation produce each an FL_KEYBOARD event. - - OS >= 10.7 contains a feature where pressing and holding certain keys opens a menu window that shows a list - of possible accented variants of this key. The selectedRange field of the FLView class and the selectedRange, insertText: - and setMarkedText: methods of the NSTextInputClient protocol are used to support this feature. - The notion of selected text (!= marked text) is monitored by the selectedRange field. - The -(NSRange)[FLView selectedRange] method is used to control whether an FLTK widget opens accented character windows - by returning .location = NSNotFound to disable that, or returning the value of the selectedRange field to enable the feature. - When selectedRange.location >= 0, the value of selectedRange.length is meaningful. 0 means no text is currently selected, - > 0 means this number of characters before the insertion point are selected. The insertText: method does - selectedRange = NSMakeRange(100, 0); to indicate no text is selected. The setMarkedText: method does - selectedRange = NSMakeRange(100, newSelection.length); to indicate that this length of text is selected. - - With OS <= 10.5, the NSView class does not implement the inputContext message. [myview process_keydown:theEvent] is - equivalent to [[FLTextInputContext singleInstance] handleEvent:theEvent]. - Method +[FLTextInputContext singleInstance] returns an instance of class FLTextInputContext that possesses - a handleEvent: method. The class FLTextView implements the so-called view's "field editor". This editor is an instance - of the FLTextView class allocated by the -(id)[FLWindowDelegate windowWillReturnFieldEditor: toObject:] method. - The -(BOOL)[FLTextInputContext handleEvent:] method emulates the missing 10.6 -(BOOL)[NSTextInputContext handleEvent:] - by sending the interpretKeyEvents: message to the FLTextView object. The system sends back doCommandBySelector: and - insertText: messages to the FLTextView object that are transmitted unchanged to myview to be processed as with OS >= 10.6. - The system also sends setMarkedText: messages directly to myview. - - There is furthermore an oddity of dead key processing with OS <= 10.5. It occurs when a dead key followed by a non-accented - key are pressed. Say, for example, that keys '^' followed by 'p' are pressed on a French or German keyboard. Resulting - messages are: [myview setMarkedText:@"^"], [myview insertText:@"^"], [myview insertText:@"p"], [FLTextView insertText:@"^p"]. - The 2nd '^' replaces the marked 1st one, followed by p^p. The resulting text in the widget is "^p^p" instead of the - desired "^p". To avoid that, the FLTextView object is deactivated by the insertText: message and reactivated after - the handleEvent: message has been processed. - - NSEvent's during a character composition sequence: - - keyDown with deadkey -> [[theEvent characters] length] is 0 - - keyUp -> [theEvent characters] contains the deadkey - - keyDown with next key -> [theEvent characters] contains the composed character - - keyUp -> [theEvent characters] contains the standard character - */ - -static void cocoaKeyboardHandler(NSEvent *theEvent) -{ - NSUInteger mods; - // get the modifiers - mods = [theEvent modifierFlags]; - // get the key code - UInt32 keyCode = 0, maskedKeyCode = 0; - unsigned short sym = 0; - keyCode = [theEvent keyCode]; - // extended keyboards can also send sequences on key-up to generate Kanji etc. codes. - // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode. - // In this mode, there seem to be no key-down codes - // printf("%08x %08x %08x\n", keyCode, mods, key); - maskedKeyCode = keyCode & 0x7f; - mods_to_e_state( mods ); // process modifier keys - if (!macKeyLookUp) macKeyLookUp = Fl_Darwin_System_Driver::compute_macKeyLookUp(); - sym = macKeyLookUp[maskedKeyCode]; - if (sym < 0xff00) { // a "simple" key - // find the result of this key without modifier - NSString *sim = [theEvent charactersIgnoringModifiers]; - UniChar one; - CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one); - // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on - if(one >= 'A' && one <= 'Z') one += 32; - if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one; - } - Fl::e_keysym = Fl::e_original_keysym = sym; - /*NSLog(@"cocoaKeyboardHandler: keycode=%08x keysym=%08x mods=%08x symbol=%@ (%@)", - keyCode, sym, mods, [theEvent characters], [theEvent charactersIgnoringModifiers]);*/ - // If there is text associated with this key, it will be filled in later. - Fl::e_length = 0; - Fl::e_text = (char*)""; -} - -@interface FLTextInputContext : NSObject // "emulates" NSTextInputContext before OS 10.6 -+ (void)initialize; -+ (FLTextInputContext*)singleInstance; --(BOOL)handleEvent:(NSEvent*)theEvent; -@end -static FLTextInputContext* fltextinputcontext_instance = nil; -@implementation FLTextInputContext -+ (void)initialize { - fltextinputcontext_instance = [[FLTextInputContext alloc] init]; -} -+ (FLTextInputContext*)singleInstance { - return fltextinputcontext_instance; -} --(BOOL)handleEvent:(NSEvent*)theEvent { - FLTextView *edit = [FLTextView singleInstance]; - [edit setActive:YES]; - [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; - [edit setActive:YES]; - return YES; -} -@end - -/* Implementation note for the support of layer-backed views. - MacOS 10.14 Mojave changes the way all drawing to displays is performed: - all NSView objects become layer-backed, that is, drawing is done by - Core Animation to a CALayer object whose content is then displayed by the NSView. - The global variable views_use_CA is set to YES when such change applies, - that is, for apps running under 10.14 and linked to SDK 10.14. - When views_use_CA is NO, views are not supposed to be layer-backed. - - Most drawing is done by [FLView drawRect:] which the system calls - when a window is created or resized and when Fl_Window_Driver::flush() runs which sends the display - message to the view. Within drawRect:, [[NSGraphicsContext currentContext] CGContext] - gives a graphics context whose product ultimately appears on screen. But the - full content of the view must be redrawn each time drawRect: runs, in contrast - to pre-10.14 where drawings were added to the previous window content. - That is why FLView maintains a bitmap (view->aux_bitmap) to which all drawing is directed. - At the end of drawRect:, the content of view->aux_bitmap is copied to the window's graphics context. - - A problem arises to support drawing done outside Fl_Window_Driver::flush(), that is, - after the app calls Fl_Window::make_current() at any time it wants. - That situation is identified by the condition (views_use_CA && !through_drawRect). - Fl_Window::make_current() thus calls [view setNeedsDisplay:YES] which instructs the system to - run drawRect: at the next event loop. Later, when drawRect: runs, the content of - aux_bitmap is copied to drawRect's graphics context. - - OpenGL windows remain processed under 10.14 as before. - */ - -@implementation FLView -- (BOOL)did_view_resolution_change { - // determine whether window is mapped to a retina display - Fl_Window *window = [(FLWindow*)[self window] getFl_Window]; - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - bool previous = d->mapped_to_retina(); - NSView *view = (!views_use_CA && window->parent() && !window->as_gl_window()) ? - [fl_xid(window->top_window()) contentView] : self; - if (view) { - NSSize s = [view convertSizeToBacking:NSMakeSize(10, 10)]; // 10.7 - d->mapped_to_retina( int(s.width + 0.5) > 10 ); - } - BOOL retval = (d->wait_for_expose_value == 0 && previous != d->mapped_to_retina()); - if (retval) { - d->changed_resolution(true); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (views_use_CA && !window->as_gl_window() ) { - [self reset_aux_bitmap]; - window->redraw(); - } -#endif - } - return retval; -} -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -- (void)create_aux_bitmap:(CGContextRef)gc retina:(BOOL)r { - if (!gc || fl_mac_os_version >= 101600) { - // bitmap context-related functions (e.g., CGBitmapContextGetBytesPerRow) can't be used here with macOS 11.0 "Big Sur" - static CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); - int W = [self frame].size.width, H = [self frame].size.height; - if (r) { W *= 2; H *= 2; } - aux_bitmap = CGBitmapContextCreate(NULL, W, H, 8, 0, cspace, kCGImageAlphaPremultipliedFirst|kCGBitmapByteOrder32Host); - } else { - aux_bitmap = CGBitmapContextCreate(NULL, CGBitmapContextGetWidth(gc), CGBitmapContextGetHeight(gc), - CGBitmapContextGetBitsPerComponent(gc), CGBitmapContextGetBytesPerRow(gc), - CGBitmapContextGetColorSpace(gc), CGBitmapContextGetBitmapInfo(gc)); - } - CGContextClearRect(aux_bitmap, CGRectMake(0, 0, - CGBitmapContextGetWidth(aux_bitmap), CGBitmapContextGetHeight(aux_bitmap))); - if (r) CGContextScaleCTM(aux_bitmap, 2, 2); -} -- (void)reset_aux_bitmap { - CGContextRelease(aux_bitmap); - aux_bitmap = NULL; -} -#endif -- (BOOL)process_keydown:(NSEvent*)theEvent -{ - return [[self inputContext] handleEvent:theEvent]; -} -- (id)initWithFrame:(NSRect)frameRect -{ - static NSInteger counter = 0; - self = [super initWithFrame:frameRect]; - if (self) { - in_key_event = NO; - identifier = ++counter; - } - return self; -} - -/* Used by all GL or non-GL windows. - * Gets called when a window is created, resized, or moved between retina and non-retina displays. - * For non-GL windows, also called by Fl_Window_Driver::flush() because of the display message sent to the view. - */ -- (void)drawRect:(NSRect)rect -{ - FLWindow *cw = (FLWindow*)[self window]; - Fl_Window *window = [cw getFl_Window]; - if (!window) return; // may happen after closing full-screen window - if (!Fl_X::flx(window)) return; // reported to happen with Gmsh (issue #434) - fl_lock_function(); - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - if (!through_Fl_X_flush -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - && (!views_use_CA || !aux_bitmap) -#endif - ) { - [self did_view_resolution_change]; - if (d->wait_for_expose_value) { - d->wait_for_expose_value = 0; - if (window->as_gl_window() && views_use_CA && fl_mac_os_version < 101401) { // 1st drawing of layer-backed GL window - window->size(window->w(), window->h()); // sends message [GLcontext update] - } - } - Fl_X *i = Fl_X::flx(window); - if ( i->region ) { - Fl_Graphics_Driver::default_driver().XDestroyRegion(i->region); - i->region = 0; - } - window->clear_damage(FL_DAMAGE_ALL); - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - CGContextRef destination = NULL; - if (views_use_CA) { - destination = [[NSGraphicsContext currentContext] CGContext]; - if (!aux_bitmap && !window->as_gl_window()) [self create_aux_bitmap:destination retina:d->mapped_to_retina()]; - } -#endif - through_drawRect = YES; - if (window->damage()) d->Fl_Window_Driver::flush(); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (destination) { // can be NULL with gl_start/gl_finish - if (fl_mac_os_version < 101600 && CGBitmapContextGetBytesPerRow(aux_bitmap) == CGBitmapContextGetBytesPerRow(destination)) { - memcpy(CGBitmapContextGetData(destination), CGBitmapContextGetData(aux_bitmap), - CGBitmapContextGetHeight(aux_bitmap) * CGBitmapContextGetBytesPerRow(aux_bitmap)); - } else { - CGImageRef img = CGBitmapContextCreateImage(aux_bitmap); - CGContextDrawImage(destination, [self frame], img); - CGImageRelease(img); - } - } -#endif - Fl_Cocoa_Window_Driver::q_release_context(); - if (!through_Fl_X_flush) window->clear_damage(); - through_drawRect = NO; - fl_unlock_function(); -} - -- (BOOL)acceptsFirstResponder -{ - return [[self window] parentWindow] ? NO : YES; // 10.2 -} -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent -{ - //NSLog(@"performKeyEquivalent:"); - /* The condition below is always false (and therefore the return statement doesn't run) - for the FLTK library unless it contains class Fl_Native_Input with which FLTK windows - may contain subviews inside their contentView. When such subview has focus, the condition - below becomes true. - */ - if ([[self window] firstResponder] != self) { - return NO; - } - NSUInteger mods = [theEvent modifierFlags]; - NSString *pure = [theEvent charactersIgnoringModifiers]; - // detect Function+e to open character palette - if ((mods & NSEventModifierFlagFunction) && [pure isEqualToString:@"e"] ) { - [NSApp orderFrontCharacterPalette:self]; - return YES; - } - fl_lock_function(); - cocoaKeyboardHandler(theEvent); - BOOL handled; - Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; - if ( (mods & NSEventModifierFlagControl) || (mods & NSEventModifierFlagCommand) ) { - NSString *s = [theEvent characters]; - if ( (mods & NSEventModifierFlagShift) && (mods & NSEventModifierFlagCommand) ) { - s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit - } - [FLView prepareEtext:s]; - Fl::compose_state = 0; - handled = Fl::handle(FL_KEYBOARD, w); - if (!handled) { - // detect Ctrl+Command+Space to open character palette, if not used before as shortcut - if ( (mods & NSEventModifierFlagControl) && (mods & NSEventModifierFlagCommand) && - !(mods & (NSEventModifierFlagShift|NSEventModifierFlagOption)) && [pure isEqualToString:@" "] ) { - [NSApp orderFrontCharacterPalette:self]; - } - } - } - else { - in_key_event = YES; - need_handle = NO; - handled = [self process_keydown:theEvent]; - if (need_handle) handled = Fl::handle(FL_KEYBOARD, w); - in_key_event = NO; - } - fl_unlock_function(); - return handled; -} -- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent -{ - Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; - Fl_Window *first = Fl::first_window(); - return (first == w || !first->modal()); -} -- (void)resetCursorRects { - Fl_Window *w = [(FLWindow*)[self window] getFl_Window]; - Fl_X *i = (w ? Fl_X::flx(w) : NULL); - if (!i) return; // fix for STR #3128 - // We have to have at least one cursor rect for invalidateCursorRectsForView - // to work, hence the "else" clause. - if (Fl_Cocoa_Window_Driver::driver(w)->cursor) - [self addCursorRect:[self frame] cursor:Fl_Cocoa_Window_Driver::driver(w)->cursor]; - else - [self addCursorRect:[self frame] cursor:[NSCursor arrowCursor]]; -} -- (void)mouseUp:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)rightMouseUp:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)otherMouseUp:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)mouseDown:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)rightMouseDown:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)otherMouseDown:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)mouseMoved:(NSEvent *)theEvent { - if (Fl::belowmouse()) cocoaMouseHandler(theEvent); -} -- (void)mouseEntered:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)mouseExited:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -#if defined(FLTK_HAVE_PEN_SUPPORT) -- (void)tabletProximity:(NSEvent *)theEvent { - cocoaTabletHandler(theEvent, true); -} -- (void)tabletPoint:(NSEvent *)theEvent { - cocoaTabletHandler(theEvent, true); -} -#endif -- (void)updateTrackingAreas { - if (![[self window] parentWindow]) { - while (true) { - NSArray *a = [self trackingAreas]; // 10.5 - if ([a count] == 0) break; - NSTrackingArea *ta = (NSTrackingArea*)[a objectAtIndex:0]; - [self removeTrackingArea:ta]; // 10.5 - } - NSTrackingArea *tracking = [[[NSTrackingArea alloc] // 10.5 - initWithRect:[self frame] - options:NSTrackingActiveAlways | - NSTrackingMouseEnteredAndExited | - NSTrackingMouseMoved - owner:self - userInfo:nil] autorelease]; - if (tracking) { - [self addTrackingArea:tracking]; // 10.5 - } - } - [super updateTrackingAreas]; -} -- (void)mouseDragged:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)rightMouseDragged:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)otherMouseDragged:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)scrollWheel:(NSEvent *)theEvent { - cocoaMouseWheelHandler(theEvent); -} -- (void)magnifyWithEvent:(NSEvent *)theEvent { - cocoaMagnifyHandler(theEvent); -} -- (void)keyDown:(NSEvent *)theEvent { - //NSLog(@"keyDown:%@",[theEvent characters]); - fl_lock_function(); - Fl_Window *window = [(FLWindow*)[theEvent window] getFl_Window]; - Fl::first_window(window); - cocoaKeyboardHandler(theEvent); - in_key_event = YES; - Fl_Widget *f = Fl::focus(); - if (f && f->as_gl_window()) { // ignore text input methods for GL windows - need_handle = YES; - [FLView prepareEtext:[theEvent characters]]; - } else { - need_handle = NO; - [self process_keydown:theEvent]; - } - if (need_handle) Fl::handle(FL_KEYBOARD, window); - in_key_event = NO; - fl_unlock_function(); -} -- (void)keyUp:(NSEvent *)theEvent { - //NSLog(@"keyUp:%@",[theEvent characters]); - if (![[theEvent window] isKindOfClass:[FLWindow class]]) // issue #1170 - return [super keyUp:theEvent]; - fl_lock_function(); - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - Fl::first_window(window); - cocoaKeyboardHandler(theEvent); - NSString *s = [theEvent characters]; - if ([s length] >= 1) [FLView prepareEtext:[s substringToIndex:1]]; - Fl::handle(FL_KEYUP,window); - fl_unlock_function(); -} -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 - typedef NSUInteger NSEventModifierFlags; -#endif -- (void)flagsChanged:(NSEvent *)theEvent { - //NSLog(@"flagsChanged: "); - fl_lock_function(); - static NSEventModifierFlags prevMods = 0; - NSEventModifierFlags mods = [theEvent modifierFlags]; - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - NSEventModifierFlags tMods = prevMods ^ mods; - int sendEvent = 0; - if ( tMods ) - { - unsigned short keycode = [theEvent keyCode]; - if (!macKeyLookUp) macKeyLookUp = Fl_Darwin_System_Driver::compute_macKeyLookUp(); - Fl::e_keysym = Fl::e_original_keysym = macKeyLookUp[keycode & 0x7f]; - if ( Fl::e_keysym ) - sendEvent = ( prevMods)sender -{ - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - update_e_xy_and_e_xy_root([self window]); - fl_dnd_target_window = target; - int ret = Fl::handle( FL_DND_ENTER, target ); - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - Fl::flush(); - return ret ? NSDragOperationCopy : NSDragOperationNone; -} -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender -{ - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - update_e_xy_and_e_xy_root([self window]); - fl_dnd_target_window = target; - int ret = Fl::handle( FL_DND_DRAG, target ); - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - // if the DND started in the same application, Fl::dnd() will not return until - // the DND operation is finished. The call below causes the drop indicator - // to be drawn correctly (a full event handling would be better...) - Fl::flush(); - return ret ? NSDragOperationCopy : NSDragOperationNone; -} -- (BOOL)performDragOperation:(id )sender -{ - static char *DragData = NULL; - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - if ( !Fl::handle( FL_DND_RELEASE, target ) ) { - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - return NO; - } - NSPasteboard *pboard; - // NSDragOperation sourceDragMask; - // sourceDragMask = [sender draggingSourceOperationMask]; - pboard = [sender draggingPasteboard]; - update_e_xy_and_e_xy_root([self window]); - if (DragData) { free(DragData); DragData = NULL; } - if ([[pboard types] containsObject:fl_filenames_pboard_type]) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - if (fl_mac_os_version >= 101300) { - NSArray *a = [pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] - options:nil]; // 10.6 - NSEnumerator *enumerator = [a objectEnumerator]; - NSURL *url; - while ((url = (NSURL*)[enumerator nextObject]) != nil) { - const char *p = [url fileSystemRepresentation]; // 10.9 - if (!DragData) { - DragData = strdup(p); - } else { - int l = (int)strlen(DragData) + (int)strlen(p) + 2; - char *drag2 = (char*)malloc(l); - snprintf(drag2, l, "%s\n%s", DragData, p); - free(DragData); - DragData = drag2; - } - } - } else -#endif - { - CFArrayRef files = (CFArrayRef)[pboard - propertyListForType:fl_filenames_pboard_type]; - CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n")); - int l = (int)CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), - kCFStringEncodingUTF8); - DragData = (char *)malloc(l + 1); - CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8); - CFRelease(all); - } - } else if ([[pboard types] containsObject:UTF8_pasteboard_type]) { - NSData *data = [pboard dataForType:UTF8_pasteboard_type]; - DragData = (char *)malloc([data length] + 1); - [data getBytes:DragData length:[data length]]; - DragData[([data length])] = 0; - Fl_Screen_Driver::convert_crlf(DragData, strlen(DragData)); - } - else { - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - return NO; - } - Fl::e_text = DragData; - Fl::e_length = (int)strlen(DragData); - int old_event = Fl::e_number; - Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); - Fl::e_number = old_event; - if (DragData) { free(DragData); DragData = NULL; } - Fl::e_text = NULL; - Fl::e_length = 0; - fl_dnd_target_window = NULL; - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - return YES; -} -- (void)draggingExited:(id < NSDraggingInfo >)sender -{ - fl_lock_function(); - if ( fl_dnd_target_window ) { - Fl::handle( FL_DND_LEAVE, fl_dnd_target_window ); - fl_dnd_target_window = 0; - } - fl_unlock_function(); -} -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal -{ - return NSDragOperationGeneric; -} - -+ (void)prepareEtext:(NSString*)aString { - // fills Fl::e_text with UTF-8 encoded aString using an adequate memory allocation - static char *received_utf8 = NULL; - static int lreceived = 0; - char *p = (char*)[aString UTF8String]; - int l = (int)strlen(p); - if (l > 0) { - if (lreceived == 0) { - received_utf8 = (char*)malloc(l + 1); - lreceived = l; - } - else if (l > lreceived) { - received_utf8 = (char*)realloc(received_utf8, l + 1); - lreceived = l; - } - strcpy(received_utf8, p); - Fl::e_text = received_utf8; - } - Fl::e_length = l; -} - -+ (void)concatEtext:(NSString*)aString { - // extends Fl::e_text with aString - NSString *newstring = [[NSString stringWithUTF8String:Fl::e_text] stringByAppendingString:aString]; - [FLView prepareEtext:newstring]; -} - -- (void)doCommandBySelector:(SEL)aSelector { - NSString *s = [[NSApp currentEvent] characters]; - //NSLog(@"doCommandBySelector:%s text='%@'",sel_getName(aSelector), s); - s = [s substringFromIndex:[s length] - 1]; - [FLView prepareEtext:s]; // use the last character of the event; necessary for deadkey + Tab - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - Fl::handle(FL_KEYBOARD, target); -} - -- (void)insertText:(id)aString { - [self insertText:aString replacementRange:NSMakeRange(NSNotFound, 0)]; -} - -- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { - NSString *received; - if ([aString isKindOfClass:[NSAttributedString class]]) { - received = [(NSAttributedString*)aString string]; - } else { - received = (NSString*)aString; - } - /*NSLog(@"insertText='%@' l=%d Fl::compose_state=%d range=%d,%d", - received,strlen([received UTF8String]),Fl::compose_state,replacementRange.location,replacementRange.length);*/ - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - if (fl_mac_os_version >= 101400 && replacementRange.length > 0) { - // occurs after a key was pressed and maintained and an auxiliary window appeared - // prevents marking dead key from deactivation - [[self inputContext] discardMarkedText]; - } - while (replacementRange.length--) { // delete replacementRange.length characters before insertion point - int saved_keysym = Fl::e_keysym; - Fl::e_keysym = FL_BackSpace; - Fl::handle(FL_KEYBOARD, target); - Fl::e_keysym = saved_keysym; - } - if (in_key_event && Fl_Cocoa_Screen_Driver::next_marked_length && Fl::e_length) { - // if setMarkedText + insertText is sent during handleEvent, text cannot be concatenated in single FL_KEYBOARD event - Fl::handle(FL_KEYBOARD, target); - Fl::e_length = 0; - } - if (in_key_event && Fl::e_length) [FLView concatEtext:received]; - else [FLView prepareEtext:received]; - Fl_Cocoa_Screen_Driver::next_marked_length = 0; - // We can get called outside of key events (e.g., from the character palette, from CJK text input). - BOOL palette = !(in_key_event || Fl::compose_state); - if (palette) Fl::e_keysym = 0; - // YES if key has text attached - BOOL has_text_key = Fl::e_keysym <= '~' || Fl::e_keysym == FL_Iso_Key || - (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last && Fl::e_keysym != FL_KP_Enter); - // insertText sent during handleEvent of a key without text cannot be processed in a single FL_KEYBOARD event. - // Occurs with deadkey followed by non-text key. Occurs also with emoji palette. - if (!in_key_event || !has_text_key) { - Fl::handle(FL_KEYBOARD, target); - Fl::e_length = 0; - } - else need_handle = YES; - selectedRange = NSMakeRange(100, 0); // 100 is an arbitrary value - // for some reason, with the palette, the window does not redraw until the next mouse move or button push - // sending a 'redraw()' or 'awake()' does not solve the issue! - if (palette) Fl::flush(); - fl_unlock_function(); -} - -- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection { - [self setMarkedText:aString selectedRange:newSelection replacementRange:NSMakeRange(NSNotFound, 0)]; -} - -- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection replacementRange:(NSRange)replacementRange { - NSString *received; - if ([aString isKindOfClass:[NSAttributedString class]]) { - received = [(NSAttributedString*)aString string]; - } else { - received = (NSString*)aString; - } - fl_lock_function(); - /*NSLog(@"setMarkedText:%@ l=%d newSelection=%d,%d Fl::compose_state=%d replacement=%d,%d", - received, strlen([received UTF8String]), newSelection.location, newSelection.length, Fl::compose_state, - replacementRange.location, replacementRange.length);*/ - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - while (replacementRange.length--) { // delete replacementRange.length characters before insertion point - Fl::e_keysym = FL_BackSpace; - Fl::compose_state = 0; - Fl_Cocoa_Screen_Driver::next_marked_length = 0; - Fl::handle(FL_KEYBOARD, target); - Fl::e_keysym = 'a'; // pretend a letter key was hit - } - if (in_key_event && Fl_Cocoa_Screen_Driver::next_marked_length && Fl::e_length) { - // if setMarkedText + setMarkedText is sent during handleEvent, text cannot be concatenated in single FL_KEYBOARD event - Fl::handle(FL_KEYBOARD, target); - Fl::e_length = 0; - } - if (in_key_event && Fl::e_length) [FLView concatEtext:received]; - else [FLView prepareEtext:received]; - Fl_Cocoa_Screen_Driver::next_marked_length = (int)strlen([received UTF8String]); - if (!in_key_event) Fl::handle( FL_KEYBOARD, target); - else need_handle = YES; - selectedRange = NSMakeRange(100, newSelection.length); - fl_unlock_function(); -} - -- (void)unmarkText { - fl_lock_function(); - Fl_Cocoa_Screen_Driver::reset_marked_text(); - fl_unlock_function(); - //NSLog(@"unmarkText"); -} - -- (NSRange)selectedRange { - Fl_Widget *w = Fl::focus(); - if (w && w->use_accents_menu()) return selectedRange; - return NSMakeRange(NSNotFound, 0); -} - -- (NSRange)markedRange { - //NSLog(@"markedRange=%d %d", Fl::compose_state > 0?0:NSNotFound, Fl::compose_state); - return NSMakeRange(Fl::compose_state > 0?0:NSNotFound, Fl::compose_state); -} - -- (BOOL)hasMarkedText { - //NSLog(@"hasMarkedText %s", Fl::compose_state > 0?"YES":"NO"); - return (Fl::compose_state > 0); -} - -- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange { - return [self attributedSubstringForProposedRange:aRange actualRange:NULL]; -} -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - //NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length); - return nil; -} - -- (NSArray *)validAttributesForMarkedText { - return nil; -} - -- (NSRect)firstRectForCharacterRange:(NSRange)aRange { - return [self firstRectForCharacterRange:aRange actualRange:NULL]; -} -- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - //NSLog(@"firstRectForCharacterRange %d %d actualRange=%p",aRange.location, aRange.length,actualRange); - NSRect glyphRect; - fl_lock_function(); - Fl_Widget *focus = Fl::focus(); - Fl_Window *wfocus = [(FLWindow*)[self window] getFl_Window]; - if (!focus) focus = wfocus; - glyphRect.size.width = 0; - - int x, y, height; - if (Fl_Cocoa_Screen_Driver::insertion_point_location(&x, &y, &height)) { - glyphRect.origin.x = (CGFloat)x; - glyphRect.origin.y = (CGFloat)y; - } else { - if (focus->as_window()) { - glyphRect.origin.x = 0; - glyphRect.origin.y = focus->h(); - } - else { - glyphRect.origin.x = focus->x(); - glyphRect.origin.y = focus->y() + focus->h(); - } - height = 12; - } - glyphRect.size.height = height; - Fl_Window *win = focus->as_window(); - if (!win) win = focus->window(); - while (win != NULL && win != wfocus) { - glyphRect.origin.x += win->x(); - glyphRect.origin.y += win->y(); - win = win->window(); - } - // Convert the rect to screen coordinates - float s = Fl_Graphics_Driver::default_driver().scale(); - glyphRect.origin.x *= s; - glyphRect.origin.y *= s; - glyphRect.origin.y = wfocus->h()*s - glyphRect.origin.y; - glyphRect.origin = [(FLWindow*)[self window] convertBaseToScreen:glyphRect.origin]; - glyphRect.size.height *= s; - if (actualRange) *actualRange = aRange; - fl_unlock_function(); - return glyphRect; -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { - return 0; -} - -- (NSInteger)windowLevel { - return [[self window] level]; -} - -- (NSInteger)conversationIdentifier { - return identifier; -} - -- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context -{ - return NSDragOperationCopy; -} -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation -{ - Fl_Widget *w = Fl::pushed(); - if ( w ) { - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_RELEASE); - Fl::e_number = old_event; - Fl::pushed( 0 ); - } -} - -@end - - -/* - * Initialize the given port for redraw and call the window's flush() to actually draw the content - */ -void Fl_Cocoa_Window_Driver::flush() -{ - if (pWindow->as_gl_window()) { - Fl_Window_Driver::flush(); - } else { - through_Fl_X_flush = YES; - NSView *view = [fl_xid(pWindow) contentView]; - if (views_use_CA) [view display]; - else { - [view setNeedsDisplay:YES]; - [view displayIfNeededIgnoringOpacity]; - } - through_Fl_X_flush = NO; - } -} - -/* - * go ahead, create that (sub)window - */ -void Fl_Cocoa_Window_Driver::makeWindow() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - Fl_Group::current(0); - fl_open_display(); - NSInteger winlevel = NSNormalWindowLevel; - NSUInteger winstyle; - Fl_Sys_Menu_Bar::create_window_menu(); // effective once at most - Fl_Window* w = pWindow; - if (w->parent()) { - w->border(0); - show_iconic(0); - } - if (w->border()) { - winstyle = (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable); - if (is_resizable()) - winstyle |= NSWindowStyleMaskResizable; - } else { - winstyle = NSWindowStyleMaskBorderless; - } - if (show_iconic() && !w->parent()) { // prevent window from being out of work area when created iconized - int sx, sy, sw, sh; - Fl::screen_work_area (sx, sy, sw, sh, w->x(), w->y()); - if (w->x() < sx) x(sx); - if (w->y() < sy) y(sy); - } - int xp = w->x(); - int yp = w->y(); - - int xwm = xp, ywm = yp, bt, bx, by; - - if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) { - // menu windows and tooltips - if (w->modal()||w->tooltip_window()) { - winlevel = modal_window_level(); - } - } - if (w->modal()) { - winstyle &= ~NSWindowStyleMaskMiniaturizable; - winlevel = modal_window_level(); - } - else if (w->non_modal()) { - winlevel = non_modal_window_level(); - } - - if (force_position()) { - if (!Fl::grab()) { - xp = xwm; yp = ywm; - x(xp);y(yp); - } - xp -= bx; - yp -= by+bt; - } - - Fl_X *x = new Fl_X; - other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows - x->region = 0; - subRect(0); - gc = 0; - mapped_to_retina(false); - changed_resolution(false); - - NSRect crect; - float s = Fl::screen_driver()->scale(0); - crect.origin.x = round(s * w->x()); // correct origin set later for subwindows - crect.origin.y = main_screen_height - round(s * (w->y() + w->h())); - crect.size.width = int(s * w->w()); - crect.size.height = int(s * w->h()); - FLWindow *cw = [[FLWindow alloc] initWithFl_W:w - contentRect:crect - styleMask:winstyle]; - [cw setFrameOrigin:crect.origin]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) { - if (!w->parent() && (winstyle & NSWindowStyleMaskTitled) && - (winstyle & NSWindowStyleMaskResizable) && !w->modal() && !w->non_modal() && - (Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() > Fl_Sys_Menu_Bar::tabbing_mode_none)) { - if (Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() == Fl_Sys_Menu_Bar::tabbing_mode_preferred) - [cw setTabbingMode:NSWindowTabbingModePreferred]; - else [cw setTabbingMode:NSWindowTabbingModeAutomatic]; - } else { - [cw setTabbingMode:NSWindowTabbingModeDisallowed]; - } - } -#endif - if (!w->parent()) { - [cw setHasShadow:YES]; - [cw setAcceptsMouseMovedEvents:YES]; - } - if (w->shape()) { - [cw setOpaque:NO]; // shaped windows must be non opaque - [cw setBackgroundColor:[NSColor clearColor]]; // and with transparent background color - } - x->xid = (fl_uintptr_t)cw; - x->w = w; - flx(x); - wait_for_expose_value = 1; - if (!w->parent()) { - x->next = Fl_X::first; - Fl_X::first = x; - } else if (Fl_X::first) { - x->next = Fl_X::first->next; - Fl_X::first->next = x; - } - else { - x->next = NULL; - Fl_X::first = x; - } - FLView *myview = [[FLView alloc] initWithFrame:crect]; - [cw setContentView:myview]; - [myview release]; - [cw setLevel:winlevel]; - - q_set_window_title(cw, w->label(), w->iconlabel()); - NSImage *icon = icon_image; // is a window or default icon present? - if (!icon) icon = ((Fl_Cocoa_Screen_Driver*)Fl::screen_driver())->default_icon; - if (icon && (winstyle & NSWindowStyleMaskTitled) && w->label() && strlen(w->label()) > 0) { - [cw setRepresentedFilename:[NSString stringWithFormat:@"/%@", [cw title]]]; - NSButton *icon_button = [cw standardWindowButton:NSWindowDocumentIconButton]; - if (icon_button) { - [icon setSize:[icon_button frame].size]; - [icon_button setImage:icon]; - } - } - if (!force_position()) { - if (w->modal()) { - [cw center]; - } else if (w->non_modal()) { - [cw center]; - } else if (!w->fullscreen_active()) { - static NSPoint delta = NSZeroPoint; - delta = [cw cascadeTopLeftFromPoint:delta]; - } - crect = [cw frame]; // synchronize FLTK's and the system's window coordinates - this->x(round(crect.origin.x/s)); - this->y( round((main_screen_height - crect.origin.y)/s) - w->h() ); - } - if(w->menu_window()) { // make menu windows slightly transparent - [cw setAlphaValue:0.97]; - } - // Install DnD handlers - [myview registerForDraggedTypes:[NSArray arrayWithObjects:UTF8_pasteboard_type, - fl_filenames_pboard_type, nil]]; - - if (pWindow->get_size_range(NULL, NULL, NULL, NULL, NULL, NULL, NULL)) size_range(); - - if ( w->border() || (!w->modal() && !w->tooltip_window()) ) { - Fl_Tooltip::enter(0); - } - - if (w->modal()) Fl::modal_ = w; - - w->set_visible(); - if ( w->border() || (!w->modal() && !w->tooltip_window() && - w->user_data() != (void*)&Fl_Screen_Driver::transient_scale_display) ) Fl::handle(FL_FOCUS, w); - [cw setDelegate:[FLWindowDelegate singleInstance]]; - if (show_iconic()) { - show_iconic(0); - w->handle(FL_SHOW); // create subwindows if any - if (fl_mac_os_version < 101300) { // TODO: threshold may be smaller - // draw the window and its subwindows before its icon is computed - [cw recursivelySendToSubwindows:@selector(display) applyToSelf:YES]; - } - [cw miniaturize:nil]; - } else if (w->parent()) { // a subwindow - [cw setIgnoresMouseEvents:YES]; // needs OS X 10.2 - // next 2 statements so a subwindow doesn't leak out of its parent window - [cw setOpaque:NO]; - [cw setBackgroundColor:[NSColor clearColor]]; // transparent background color - starting_moved_window = w; - [cw setSubwindowFrame]; - starting_moved_window = NULL; - // needed if top window was first displayed miniaturized - FLWindow *pxid = fl_xid(w->top_window()); - [pxid makeFirstResponder:[pxid contentView]]; - } else { // a top-level window - if ([cw canBecomeKeyWindow]) [cw makeKeyAndOrderFront:nil]; - else [cw orderFront:nil]; - if (w->fullscreen_active()) { - if (fullscreen_screen_top() >= 0) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) - cw.collectionBehavior |= NSWindowCollectionBehaviorFullScreenNone; -#endif - *no_fullscreen_x() = pWindow->x(); - *no_fullscreen_y() = pWindow->y(); - } - fullscreen_on(); - } - } - if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() && !w->parent() && w->border() && - !w->modal() && !w->non_modal()) { - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->new_window(w); - } - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_SHOW); - Fl::e_number = old_event; - - // if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); } - if (!w->parent()) [myview did_view_resolution_change]; // to set mapped_to_retina to its current state - [pool release]; -} - - -static BOOL fullscreen_screen_border = NO; // YES means the multi-screened window had a border before - - -static NSUInteger calc_win_style(Fl_Window *win); - - -void Fl_Cocoa_Window_Driver::fullscreen_on() { - pWindow->_set_fullscreen(); - bool has_border = pWindow->border(); - if (fullscreen_screen_top() >= 0 && has_border) { - fullscreen_screen_border = YES; - has_border = false; - } - if (has_border) { - NSWindow *nswin = fl_xid(pWindow); -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - if (fl_mac_os_version >= 101300) { - NSWindow *active_tab = [[nswin tabGroup] selectedWindow]; - if (active_tab) nswin = active_tab; - } -# endif - if (fullscreen_screen_border) { // from "All Screens" fullscreen to single-screen fullscreen - pWindow->_clear_fullscreen(); - [nswin setLevel:NSNormalWindowLevel]; - [nswin setStyleMask:calc_win_style(pWindow)]; //10.6 - pWindow->_set_fullscreen(); - } - [nswin toggleFullScreen:nil]; - } else { - FLWindow *nswin = fl_xid(pWindow); - if (nswin.styleMask & NSWindowStyleMaskFullScreen) { - // from single-screen fullscreen to "All Screens" fullscreen, with border -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) { - bool allscreens_on = (nswin.collectionBehavior & NSWindowCollectionBehaviorFullScreenNone); - if (allscreens_on) nswin.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenNone; - [nswin toggleFullScreen:nil]; - if (allscreens_on) nswin.collectionBehavior |= NSWindowCollectionBehaviorFullScreenNone; - } else -#endif - [nswin toggleFullScreen:nil]; - if (*no_fullscreen_w() == 0) { - *no_fullscreen_x() = x(); - *no_fullscreen_y() = y(); - *no_fullscreen_w() = w(); - *no_fullscreen_h() = h(); - } - pWindow->_set_fullscreen(); - } - [nswin setStyleMask:NSWindowStyleMaskBorderless]; // 10.6 - if ([nswin isKeyWindow]) { - if ([nswin level] != NSStatusWindowLevel) { - [nswin setLevel:NSStatusWindowLevel]; - fixup_window_levels(); - } - } else if([nswin level] != NSNormalWindowLevel) { - [nswin setLevel:NSNormalWindowLevel]; - fixup_window_levels(); - } - int sx, sy, sw, sh, X, Y, W, H; - int top = fullscreen_screen_top(); - int bottom = fullscreen_screen_bottom(); - int left = fullscreen_screen_left(); - int right = fullscreen_screen_right(); - if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) { - top = Fl::screen_num(x(), y(), w(), h()); - bottom = top; - left = top; - right = top; - } - Fl::screen_xywh(sx, sy, sw, sh, top); - Y = sy; - Fl::screen_xywh(sx, sy, sw, sh, bottom); - H = sy + sh - Y; - Fl::screen_xywh(sx, sy, sw, sh, left); - X = sx; - Fl::screen_xywh(sx, sy, sw, sh, right); - W = sx + sw - X; - pWindow->resize(X, Y, W, H); - } - Fl::handle(FL_FULLSCREEN, pWindow); -} - - -void Fl_Cocoa_Window_Driver::maximize() { - if (border()) [fl_xid(pWindow) performZoom:nil]; - else Fl_Window_Driver::maximize(); -} - - -void Fl_Cocoa_Window_Driver::un_maximize() { - if (border()) [fl_xid(pWindow) performZoom:nil]; - else Fl_Window_Driver::un_maximize(); -} - - -static NSUInteger calc_win_style(Fl_Window *win) { - NSUInteger winstyle; - if (win->border() && !win->fullscreen_active()) { - winstyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; - if (Fl_Window_Driver::driver(win)->is_resizable()) winstyle |= NSWindowStyleMaskResizable; - if (!win->modal()) winstyle |= NSWindowStyleMaskMiniaturizable; - } else winstyle = NSWindowStyleMaskBorderless; - return winstyle; -} - -static void restore_window_title_and_icon(Fl_Window *pWindow, NSImage *icon) { - FLWindow *nswin = fl_xid(pWindow); - q_set_window_title(nswin, pWindow->label(), pWindow->iconlabel()); - if (!icon) icon = ((Fl_Cocoa_Screen_Driver*)Fl::screen_driver())->default_icon; - if (icon && ([nswin styleMask] & NSWindowStyleMaskTitled) && pWindow->label() && - (strlen(pWindow->label()) > 0)) { - NSButton *icon_button = [nswin standardWindowButton:NSWindowDocumentIconButton]; - if (icon_button) { - [icon setSize:[icon_button frame].size]; - [icon_button setImage:icon]; - } - } -} - - -void Fl_Cocoa_Window_Driver::fullscreen_off(int X, int Y, int W, int H) { - NSWindow *nswin = fl_xid(pWindow); - pWindow->_clear_fullscreen(); - if ([nswin styleMask] & NSWindowStyleMaskFullScreen) { -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - if (fl_mac_os_version >= 101300) { - NSWindow *active_tab = [[nswin tabGroup] selectedWindow]; - if (active_tab) nswin = active_tab; - } -# endif - [nswin toggleFullScreen:nil]; - pWindow->resize(*no_fullscreen_x(), *no_fullscreen_y(), *no_fullscreen_w(), *no_fullscreen_h()); - } else { - // Transition from multi-screen fullscreen mode to normal mode - NSInteger level = NSNormalWindowLevel; - if (pWindow->modal()) level = modal_window_level(); - else if (pWindow->non_modal()) level = non_modal_window_level(); - /* Hide (orderOut) and later show (orderFront) the window to avoid a crash that - occurs in a very specific situation: the dock is at bottom and - H is larger than the maximum value for the display. - See "Crashing regression in MacOS code" in fltk.coredev. - */ - BOOL has_focus = [nswin isKeyWindow]; - [nswin orderOut:nil]; - [nswin setLevel:level]; - [nswin setStyleMask:calc_win_style(pWindow)]; //10.6 - restore_window_title_and_icon(pWindow, icon_image); - pWindow->resize(X, Y, W, H); - if (pWindow->maximize_active()) Fl_Window_Driver::maximize(); - if (has_focus) [nswin makeKeyAndOrderFront:nil]; - else [nswin orderFront:nil]; - } - Fl::handle(FL_FULLSCREEN, pWindow); - fullscreen_screen_border = NO; -} - - -void Fl_Cocoa_Window_Driver::fullscreen_screens(bool on_off) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) { - FLWindow *xid = fl_mac_xid(pWindow); - if (on_off) xid.collectionBehavior |= NSWindowCollectionBehaviorFullScreenNone; - else xid.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenNone; - } -#endif -} - - -void Fl_Cocoa_Window_Driver::use_border() { - if (!shown() || pWindow->parent()) return; - if (pWindow->fullscreen_active() || pWindow->maximize_active()) { - // prevent changing border while window is fullscreen or maximized - static bool active = false; - if (!active) { - active = true; - bool b = !border(); - pWindow->border(b); - active = false; - } - return; - } - [fl_xid(pWindow) setStyleMask:calc_win_style(pWindow)]; // 10.6 - if (border()) restore_window_title_and_icon(pWindow, icon_image); - pWindow->redraw(); -} - -/* - * Tell the OS what window sizes we want to allow - */ -void Fl_Cocoa_Window_Driver::size_range() { - Fl_X *i = Fl_X::flx(pWindow); - if (i && i->xid) { - float s = Fl::screen_driver()->scale(0); - int bt = get_window_frame_sizes(pWindow); - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - NSSize minSize = NSMakeSize(int(minw * s +.5) , int(minh * s +.5) + bt); - NSSize maxSize = NSMakeSize(maxw ? int(maxw * s + .5):32000, maxh ? int(maxh * s +.5) + bt:32000); - [(FLWindow*)i->xid setMinSize:minSize]; - [(FLWindow*)i->xid setMaxSize:maxSize]; - } -} - -void Fl_Cocoa_Window_Driver::wait_for_expose() -{ - if (fl_mac_os_version < 101300) { - [fl_xid(pWindow) recursivelySendToSubwindows:@selector(waitForExpose) applyToSelf:YES]; - } else { - Fl_Window_Driver::wait_for_expose(); - } -} - -/* - * set the window title bar name - */ -void Fl_Cocoa_Window_Driver::label(const char *name, const char *mininame) { - if (shown() || Fl_X::flx(pWindow)) { - q_set_window_title(fl_xid(pWindow), name, mininame); - if (fl_sys_menu_bar && Fl_Sys_Menu_Bar_Driver::window_menu_style()) - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->rename_window(pWindow); - } -} - - -/* - * make a window visible - */ -void Fl_Cocoa_Window_Driver::show() { - Fl_X *top = NULL; - if (parent()) top = Fl_X::flx(pWindow->top_window()); - if (!shown() && (!parent() || (top && ![(FLWindow*)top->xid isMiniaturized]))) { - makeWindow(); - } else { - if ( !parent() ) { - Fl_X *i = Fl_X::flx(pWindow); - if ([(FLWindow*)i->xid isMiniaturized]) { - i->w->redraw(); - [(FLWindow*)i->xid deminiaturize:nil]; - } - if (!fl_capture) { - [(FLWindow*)i->xid makeKeyAndOrderFront:nil]; - } - } - else pWindow->set_visible(); - } -} - -/* - * resize a window - */ -void Fl_Cocoa_Window_Driver::resize(int X, int Y, int W, int H) { - if (!pWindow->shown() && (X != x() || Y != y())) force_position(1); - if (view_resized() || !visible_r()) { - pWindow->Fl_Group::resize(X, Y, W, H); - if (!pWindow->shown()) pWindow->init_sizes(); - } else if (!through_resize()) { - NSPoint pt = FLTKtoCocoa(pWindow, X, Y, H); - FLWindow *xid = fl_xid(pWindow); - through_resize(1); - if (W != w() || H != h() || Fl_Window::is_a_rescale()) { - NSRect r; - float s = Fl::screen_driver()->scale(screen_num()); - int bt = get_window_frame_sizes(pWindow); - r.origin = pt; - r.size.width = round(W*s); - r.size.height = round(H*s) + bt; - if (NSEqualRects(r, [xid frame])) { - pWindow->Fl_Group::resize(X, Y, W, H); // runs rarely, e.g. with scaled down test/tabs - pWindow->redraw(); - } else { - // First resize the logical FLTK coordinates for this and all children - if (!Fl_Window::is_a_rescale()) - pWindow->Fl_Group::resize(X, Y, W, H); - // Next update the physical Cocoa view - [xid setFrame:r display:YES]; - [[xid contentView] displayIfNeededIgnoringOpacity]; - // Finally tell the the group to render its contents if the code above - // didn't already - pWindow->redraw(); - } - } - else { - if (pWindow->parent()) starting_moved_window = pWindow; - if (!NSEqualPoints([xid frame].origin, pt)) - [xid setFrameOrigin:pt]; // set cocoa coords to FLTK position - else { - x(X); y(Y); - } - if (pWindow->parent()) starting_moved_window = NULL; - } - through_resize(0); - } - - // make sure subwindow doesn't leak outside parent - if (pWindow->parent()) [fl_xid(pWindow) checkSubwindowFrame]; -} - - -/* - * make all drawing go into this window (called by subclass flush() impl.) - - This can be called in 3 different situations: - - 1) When a window is created, resized or moved between low/high resolution displays. - macOS sends the drawRect: message to the window view after having prepared the - current graphics context to draw to this view. The drawRect: method sets through_drawRect - to YES and calls Fl_Window_Driver::flush(). Fl_Window_Driver::flush() calls - Fl_Window::flush() that calls Fl_Window::make_current() that uses the graphics - context of the window or the layer. The window's draw() function is then executed. - - 2) At each round of the FLTK event loop. - Fl::flush() is called, that calls Fl_Cocoa_Window_Driver::flush() on each window that needs drawing. - Fl_Cocoa_Window_Driver::flush() sets through_Fl_X_Flush to YES and marks the view as - needing display. The view is sent the displayIfNeededIgnoringOpacity or display message which makes - the OS send the view the drawRect: message. The program proceeds next as in 1) above. - - 3) An FLTK application can call Fl_Window::make_current() at any time before it draws to a window. - This occurs for instance in the idle callback function of the mandelbrot test program. Variables - through_Fl_X_flush and through_drawRect equal NO. - Before 10.14: The window graphics context is obtained. Subsequent drawing requests go to the window. - After 10.14: The layered view is marked as needing display. It will be sent the drawRect: message - at the next event loop. Subsequent drawing operations, until drawRect: runs, are sent to view->aux_bitmap. - - CAUTION: it's not possible to call Fl::wait(), Fl::check() nor Fl::ready() while in the draw() - function of a widget. Use an idle callback instead. - */ -void Fl_Cocoa_Window_Driver::make_current() -{ - q_release_context(); - Fl_X *i = Fl_X::flx(pWindow); - //NSLog(@"region-count=%d damage=%u",i->region?i->region->count:0, pWindow->damage()); - fl_window = (FLWindow*)i->xid; - ((Fl_Quartz_Graphics_Driver&)Fl_Graphics_Driver::default_driver()).high_resolution( mapped_to_retina() ); - - if (pWindow->as_overlay_window() && other_xid && changed_resolution()) { - destroy_double_buffer(); - changed_resolution(false); - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - FLView *view = (FLView*)[fl_window contentView]; - if (views_use_CA && !through_drawRect) { // detect direct calls from the app - [view setNeedsDisplay:YES]; - } - if (views_use_CA && view->aux_bitmap) { - gc = view->aux_bitmap; - } else -#endif - { -// ignore deprecation warning of "graphicsContextWithWindow" because used only with 10.13 or before -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSGraphicsContext *nsgc = (through_drawRect ? [NSGraphicsContext currentContext] : - [NSGraphicsContext graphicsContextWithWindow:fl_window]); -#pragma clang diagnostic pop - static SEL gc_sel = fl_mac_os_version >= 101000 ? @selector(CGContext) : @selector(graphicsPort); - gc = (CGContextRef)[nsgc performSelector:gc_sel]; - } - Fl_Graphics_Driver::default_driver().gc(gc); -#if defined(FLTK_HAVE_CAIROEXT) - CGContextSaveGState(gc); // one extra level -#endif - CGContextSaveGState(gc); // native context - // antialiasing must be deactivated because it applies to rectangles too - // and escapes even clipping!!! - // it gets activated when needed (e.g., draw text) - CGContextSetShouldAntialias(gc, false); - CGFloat hgt = [[fl_window contentView] frame].size.height; - float s = Fl::screen_driver()->scale(0); - CGContextTranslateCTM(gc, 0.5f*s, hgt-0.5f*s); - CGContextScaleCTM(gc, 1.0f, -1.0f); // now 0,0 is top-left point of the window - CGContextScaleCTM(gc, s, s); // apply current scaling factor - // for subwindows, limit drawing to inside of parent window - // half pixel offset is necessary for clipping as done by fl_cgrectmake_cocoa() - if (subRect()) { - CGContextClipToRect(gc, CGRectOffset(*(subRect()), -0.5, -0.5)); - } -// this is the context with origin at top left of (sub)window - CGContextSaveGState(gc); - fl_clip_region( 0 ); -#ifdef FLTK_HAVE_CAIROEXT - // update the cairo_t context - if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow); -#endif -} - -// Give the Quartz context back to the system -void Fl_Cocoa_Window_Driver::q_release_context(Fl_Cocoa_Window_Driver *x) { - CGContextRef gc = (CGContextRef)Fl_Graphics_Driver::default_driver().gc(); - if (x && x->shown() && x->gc != gc) return; - if (!gc) return; - CGContextRestoreGState(gc); // match the CGContextSaveGState's of make_current - CGContextRestoreGState(gc); - CGContextFlush(gc); - Fl_Graphics_Driver::default_driver().gc(0); -#if defined(FLTK_HAVE_CAIROEXT) - CGContextRestoreGState(gc); -#endif -} - - -static NSBitmapImageRep *pdf_to_nsbitmapimagerep(NSData *pdfdata) { - NSImage *image = [[NSImage alloc] initWithData:pdfdata]; - NSInteger width = [image size].width * 2; - NSInteger height = [image size].height * 2; - NSBitmapImageRep *bitmap = [NSBitmapImageRep alloc]; - NSRect dest_r = NSMakeRect(0, 0, width, height); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 - if (fl_mac_os_version >= 100900) { - // This procedure is necessary because initWithFocusedViewRect is deprecated in macOS 10.14 - // and because it produces a bitmap with floating point pixel values with macOS 11.x - bitmap = [bitmap initWithBitmapDataPlanes:NULL - pixelsWide:width - pixelsHigh:height - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:0 - bitsPerPixel:0]; - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - [NSGraphicsContext saveGraphicsState]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];// 10.4 - [[NSColor clearColor] set]; - NSRect r = NSMakeRect(0, 0, width, height); - NSRectFill(r); - [image drawInRect:dest_r]; // 10.9 - [NSGraphicsContext restoreGraphicsState]; - [localPool release]; - } else -#endif - { - [image lockFocus]; - // the deprecation warning at 10.14 can be ignored because runs only for macOS < 10.9 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - bitmap = [bitmap initWithFocusedViewRect:dest_r]; -#pragma clang diagnostic pop - [image unlockFocus]; - } - [bitmap setSize:[image size]]; - [image release]; - return bitmap; -} - - -Fl_Quartz_Copy_Surface_Driver::~Fl_Quartz_Copy_Surface_Driver() -{ - CGContextRestoreGState(gc); - CGContextEndPage(gc); - CGPDFContextClose(gc); // needs 10.5, necessary with macOS 10.15 - CGContextRelease(gc); - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - [clip declareTypes:[NSArray arrayWithObjects:PDF_pasteboard_type, TIFF_pasteboard_type, nil] owner:nil]; - [clip setData:(NSData*)pdfdata forType:PDF_pasteboard_type]; - - //second, transform this PDF to a bitmap image and put it as tiff in clipboard with retina resolution - NSBitmapImageRep *bitmap = pdf_to_nsbitmapimagerep((NSData*)pdfdata); - CFRelease(pdfdata); - [clip setData:[bitmap TIFFRepresentation] forType:TIFF_pasteboard_type]; - [bitmap release]; - delete driver(); -} - -//////////////////////////////////////////////////////////////// -// Copy & Paste fltk implementation. -//////////////////////////////////////////////////////////////// - -// clipboard variables definitions : -char *fl_selection_buffer[2] = {NULL, NULL}; -int fl_selection_length[2] = {0, 0}; -static int fl_selection_buffer_length[2]; - -extern void fl_trigger_clipboard_notify(int source); - -static void clipboard_check(void) -{ - static NSInteger oldcount = -1; - NSInteger newcount = [[NSPasteboard generalPasteboard] changeCount]; - if (newcount == oldcount) return; - oldcount = newcount; - fl_trigger_clipboard_notify(1); -} - -static void resize_selection_buffer(int len, int clipboard) { - if (len <= fl_selection_buffer_length[clipboard]) - return; - delete[] fl_selection_buffer[clipboard]; - fl_selection_buffer[clipboard] = new char[len+100]; - fl_selection_buffer_length[clipboard] = len+100; -} - -/* - * create a selection - * stuff: pointer to selected data - * len: size of selected data - * type: always "plain/text" for now - */ -void Fl_Cocoa_Screen_Driver::copy(const char *stuff, int len, int clipboard, const char *type) { - if (!stuff || len<0) return; - if (clipboard >= 2) - clipboard = 1; // Only on X11 do multiple clipboards make sense. - - resize_selection_buffer(len+1, clipboard); - memcpy(fl_selection_buffer[clipboard], stuff, len); - fl_selection_buffer[clipboard][len] = 0; // needed for direct paste - fl_selection_length[clipboard] = len; - if (clipboard) { - CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len); - if (text==NULL) return; // there was a pb creating the object, abort. - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - [clip declareTypes:[NSArray arrayWithObject:UTF8_pasteboard_type] owner:nil]; - [clip setData:(NSData*)text forType:UTF8_pasteboard_type]; - CFRelease(text); - } -} - -static int get_plain_text_from_clipboard(int clipboard) -{ - NSInteger length = 0; - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - NSString *found = [clip availableTypeFromArray:[NSArray arrayWithObjects:UTF8_pasteboard_type, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]]; - if (found) { - NSData *data = [clip dataForType:found]; - if (data) { - NSInteger len; - char *aux_c = NULL; - if (![found isEqualToString:UTF8_pasteboard_type]) { - NSString *auxstring; - auxstring = (NSString *)CFStringCreateWithBytes(NULL, (const UInt8*)[data bytes], - [data length], - ([found isEqualToString:@"public.utf16-plain-text"] ? - kCFStringEncodingUnicode : kCFStringEncodingMacRoman), false); - aux_c = fl_strdup([auxstring UTF8String]); - [auxstring release]; - len = strlen(aux_c) + 1; - } - else len = [data length] + 1; - resize_selection_buffer((int)len, clipboard); - if (![found isEqualToString:UTF8_pasteboard_type]) { - strcpy(fl_selection_buffer[clipboard], aux_c); - free(aux_c); - } - else { - [data getBytes:fl_selection_buffer[clipboard] length:[data length]]; - } - fl_selection_buffer[clipboard][len - 1] = 0; - length = Fl_Screen_Driver::convert_crlf(fl_selection_buffer[clipboard], len - 1); // turn all \r characters into \n: - Fl::e_clipboard_type = Fl::clipboard_plain_text; - } - } - return (int)length; -} - -static Fl_RGB_Image* get_image_from_clipboard(Fl_Widget *receiver) -{ - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - NSArray *present = [clip types]; // types in pasteboard in order of decreasing preference - NSArray *possible = [NSArray arrayWithObjects:PDF_pasteboard_type, TIFF_pasteboard_type, PICT_pasteboard_type, nil]; - NSString *found = nil; - NSUInteger rank; - for (NSUInteger i = 0; (!found) && i < [possible count]; i++) { - for (rank = 0; rank < [present count]; rank++) { // find first of possible types present in pasteboard - if ([[present objectAtIndex:rank] isEqualToString:[possible objectAtIndex:i]]) { - found = [present objectAtIndex:rank]; - break; - } - } - } - if (!found) return NULL; - NSData *data = [clip dataForType:found]; - if (!data) return NULL; - NSBitmapImageRep *bitmap = nil; - if ([found isEqualToString:TIFF_pasteboard_type]) { - bitmap = [[NSBitmapImageRep alloc] initWithData:data]; - } - else if ([found isEqualToString:PDF_pasteboard_type] || [found isEqualToString:PICT_pasteboard_type]) { - bitmap = pdf_to_nsbitmapimagerep(data); - } - if (!bitmap) return NULL; - int bytesPerPixel((int)[bitmap bitsPerPixel]/8); - int bpr((int)[bitmap bytesPerRow]); - int hh((int)[bitmap pixelsHigh]); - int ww((int)[bitmap pixelsWide]); - uchar *imagedata = new uchar[bpr * hh]; - memcpy(imagedata, [bitmap bitmapData], bpr * hh); - Fl_RGB_Image *image = new Fl_RGB_Image(imagedata, ww, hh, bytesPerPixel, (bpr == ww * bytesPerPixel ? 0 : bpr) ); - image->scale([bitmap size].width, [bitmap size].height); - image->alloc_array = 1; - [bitmap release]; - Fl::e_clipboard_type = Fl::clipboard_image; - return image; -} - -// Call this when a "paste" operation happens: -void Fl_Cocoa_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) { - if (type[0] == 0) type = Fl::clipboard_plain_text; - if (clipboard) { - Fl::e_clipboard_type = ""; - if (strcmp(type, Fl::clipboard_plain_text) == 0) { - fl_selection_length[1] = get_plain_text_from_clipboard(1); - } - else if (strcmp(type, Fl::clipboard_image) == 0) { - Fl::e_clipboard_data = get_image_from_clipboard(&receiver); - if (Fl::e_clipboard_data) { - int done = receiver.handle(FL_PASTE); - Fl::e_clipboard_type = ""; - if (done == 0) { - delete (Fl_Image*)Fl::e_clipboard_data; - Fl::e_clipboard_data = NULL; - } - } - return; - } - else - fl_selection_length[1] = 0; - } - Fl::e_text = fl_selection_buffer[clipboard]; - Fl::e_length = fl_selection_length[clipboard]; - if (!Fl::e_length) Fl::e_text = (char *)""; - receiver.handle(FL_PASTE); -} - -int Fl_Cocoa_Screen_Driver::clipboard_contains(const char *type) { - NSString *found = nil; - if (strcmp(type, Fl::clipboard_plain_text) == 0) { - found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:UTF8_pasteboard_type, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]]; - } - else if (strcmp(type, Fl::clipboard_image) == 0) { - found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:TIFF_pasteboard_type, PDF_pasteboard_type, PICT_pasteboard_type, nil]]; - } - return found != nil; -} - -void Fl_Cocoa_Window_Driver::destroy(FLWindow *xid) { - [[xid parentWindow] removeChildWindow:xid]; // necessary until 10.6 at least - if (fl_sys_menu_bar && Fl_Sys_Menu_Bar_Driver::window_menu_style()) - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->remove_window([xid getFl_Window]); - [xid close]; -} - - -void Fl_Cocoa_Window_Driver::map() { - FLWindow *xid = fl_xid(pWindow); - if (pWindow && xid && ![xid parentWindow]) { // 10.2 - // after a subwindow has been unmapped, it has lost its parent window and its frame may be wrong - [xid setSubwindowFrame]; - } - if (cursor) { - [cursor release]; - cursor = NULL; - } -} - - -void Fl_Cocoa_Window_Driver::unmap() { - FLWindow *xid = fl_xid(pWindow); - if (pWindow && xid) { - if (parent()) [[xid parentWindow] removeChildWindow:xid]; // necessary with at least 10.5 - [xid orderOut:nil]; - } -} - - -void Fl_Cocoa_Window_Driver::iconize() { - [fl_xid(pWindow) miniaturize:nil]; -} - -static NSImage *CGBitmapContextToNSImage(CGContextRef c) -// the returned NSImage is autoreleased -{ - CGImageRef cgimg = CGBitmapContextCreateImage(c); // requires 10.4 - NSImage* image = [[NSImage alloc] initWithCGImage:cgimg size:NSZeroSize]; // requires 10.6 - CFRelease(cgimg); - return [image autorelease]; -} - -int Fl_Cocoa_Window_Driver::set_cursor(Fl_Cursor c) -{ - if (cursor) { - [(NSCursor*)cursor release]; - cursor = NULL; - } - - switch (c) { - case FL_CURSOR_ARROW: cursor = [NSCursor arrowCursor]; break; - case FL_CURSOR_CROSS: cursor = [NSCursor crosshairCursor]; break; - case FL_CURSOR_INSERT: cursor = [NSCursor IBeamCursor]; break; - case FL_CURSOR_HAND: cursor = [NSCursor pointingHandCursor]; break; - case FL_CURSOR_MOVE: cursor = [NSCursor openHandCursor]; break; - case FL_CURSOR_NS: cursor = [NSCursor resizeUpDownCursor]; break; - case FL_CURSOR_WE: cursor = [NSCursor resizeLeftRightCursor]; break; - case FL_CURSOR_N: cursor = [NSCursor resizeUpCursor]; break; - case FL_CURSOR_E: cursor = [NSCursor resizeRightCursor]; break; - case FL_CURSOR_W: cursor = [NSCursor resizeLeftCursor]; break; - case FL_CURSOR_S: cursor = [NSCursor resizeDownCursor]; break; - default: - return 0; - } - - [(NSCursor*)cursor retain]; - - [fl_xid(pWindow) invalidateCursorRectsForView:[fl_xid(pWindow) contentView]]; - - return 1; -} - -int Fl_Cocoa_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { - if (cursor) { - [(NSCursor*)cursor release]; - cursor = NULL; - } - - if ((hotx < 0) || (hotx >= image->w())) - return 0; - if ((hoty < 0) || (hoty >= image->h())) - return 0; - - if (image->as_svg_image()) { - Fl_RGB_Image *image2 = (Fl_RGB_Image*)image->copy(); - image2->normalize(); - image = image2; - } - // OS X >= 10.6 can create a NSImage from a CGImage, but we need to - // support older versions, hence this pesky handling. - - NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:NULL - pixelsWide:image->data_w() - pixelsHigh:image->data_h() - bitsPerSample:8 - samplesPerPixel:image->d() - hasAlpha:!(image->d() & 1) - isPlanar:NO - colorSpaceName:(image->d() <= 2 ? - NSDeviceWhiteColorSpace : NSDeviceRGBColorSpace) - bytesPerRow:(image->data_w() * image->d()) - bitsPerPixel:(image->d()*8)]; - - // Alpha needs to be premultiplied for this format - - const uchar *i = (const uchar*)*image->data(); - const int extra_data = image->ld() ? (image->ld() - image->data_w() * image->d()) : 0; - unsigned char *o = [bitmap bitmapData]; - for (int y = 0;y < image->data_h();y++) { - if (!(image->d() & 1)) { - for (int x = 0;x < image->data_w();x++) { - unsigned int alpha; - if (image->d() == 4) { - alpha = i[3]; - *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); - *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); - } - - alpha = i[1]; - *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); - *o++ = alpha; - i++; - } - } else { - // No alpha, so we can just copy everything directly. - int len = image->data_w() * image->d(); - memcpy(o, i, len); - o += len; - i += len; - } - i += extra_data; - } - - NSImage *nsimage = [[NSImage alloc] - initWithSize:NSMakeSize(image->w(), image->h())]; - - [nsimage addRepresentation:bitmap]; - - cursor = [[NSCursor alloc] - initWithImage:nsimage - hotSpot:NSMakePoint(hotx, hoty)]; - - [fl_xid(pWindow) invalidateCursorRectsForView:[fl_xid(pWindow) contentView]]; - - [bitmap release]; - [nsimage release]; - if (image->as_svg_image()) delete image; - - return 1; -} - -@interface PrintWithTitlebarItem : NSMenuItem { -} -- (void) toggleCallback; -@end - -@implementation PrintWithTitlebarItem -- (void) toggleCallback { - NSMenuItem *item = [self representedObject]; - const char *title; - if ([self state] == NSControlStateValueOn) { - [self setState:NSControlStateValueOff]; - title = Fl_Mac_App_Menu::print_no_titlebar; - } else { - [self setState:NSControlStateValueOn]; - title = Fl_Mac_App_Menu::print; - } - [item setTitle:NSLocalizedString([NSString stringWithUTF8String:title], nil)]; -} -@end - -static PrintWithTitlebarItem *print_with_titlebar_item = NULL; - -@interface FLaboutItemTarget : NSObject -{ -} -- (BOOL)validateMenuItem:(NSMenuItem *)item; -- (void)showPanel; -- (void)printPanel; -- (void)terminate:(id)sender; -@end -@implementation FLaboutItemTarget -- (BOOL)validateMenuItem:(NSMenuItem *)item -{ // invalidate the Quit item of the application menu when running modal or when in native file chooser - if ([[NSApp keyWindow] isKindOfClass:[NSSavePanel class]]) return NO; - if (!Fl::modal() || [item action] != @selector(terminate:)) return YES; - return NO; -} -- (void)showPanel -{ - NSDictionary *options; - options = [NSDictionary dictionaryWithObjectsAndKeys: - [[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@" GUI with FLTK %d.%d", - FL_MAJOR_VERSION, FL_MINOR_VERSION ]] autorelease], @"Credits", - nil]; - [NSApp orderFrontStandardAboutPanelWithOptions:options]; -} -- (void)printPanel -{ - bool grab_decoration = ([print_with_titlebar_item state] == NSControlStateValueOn); - fl_lock_function(); - fl_print_or_copy_window(Fl::first_window(), grab_decoration, 1); - fl_unlock_function(); -} -- (void)terminate:(id)sender -{ - [NSApp terminate:sender]; -} -@end - -static void createAppleMenu(void) -{ - static BOOL donethat = NO; - if (donethat) return; - donethat = YES; - NSMenu *mainmenu, *services = nil, *appleMenu; - NSMenuItem *menuItem; - NSString *title; - - SEL infodictSEL = (fl_mac_os_version >= 100200 ? @selector(localizedInfoDictionary) : @selector(infoDictionary)); - NSString *nsappname = [[[NSBundle mainBundle] performSelector:infodictSEL] objectForKey:@"CFBundleName"]; - if (nsappname == nil) - nsappname = [[NSProcessInfo processInfo] processName]; - appleMenu = [[NSMenu alloc] initWithTitle:@""]; - /* Add menu items */ - title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::about],nil), nsappname]; - menuItem = [appleMenu addItemWithTitle:title action:@selector(showPanel) keyEquivalent:@""]; - FLaboutItemTarget *about = [[FLaboutItemTarget alloc] init]; - [menuItem setTarget:about]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - // Print front window - title = NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::print], nil); - if ([title length] > 0) { - menuItem = [appleMenu - addItemWithTitle:title - action:@selector(printPanel) - keyEquivalent:@""]; - [menuItem setTarget:about]; - [menuItem setEnabled:YES]; - // Toggle "Print Window with titlebar" / "Print Window" - title = NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::toggle_print_titlebar], nil); - print_with_titlebar_item = [[PrintWithTitlebarItem alloc] initWithTitle:title - action:@selector(toggleCallback) - keyEquivalent:@""]; - [appleMenu addItem:print_with_titlebar_item]; - [print_with_titlebar_item setTarget:print_with_titlebar_item]; - [print_with_titlebar_item setRepresentedObject:menuItem]; - [print_with_titlebar_item setState:NSControlStateValueOn]; - [print_with_titlebar_item setEnabled:YES]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - } - // Services Menu - services = [[NSMenu alloc] initWithTitle:@""]; - menuItem = [appleMenu - addItemWithTitle:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::services], nil) - action:nil - keyEquivalent:@""]; - [appleMenu setSubmenu:services forItem:menuItem]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - // Hide AppName - title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::hide],nil), nsappname]; - [appleMenu addItemWithTitle:title - action:@selector(hide:) - keyEquivalent:@"h"]; - // Hide Others - menuItem = [appleMenu - addItemWithTitle:NSLocalizedString( - [NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others] , nil) - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"]; - [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; - // Show All - [appleMenu addItemWithTitle:NSLocalizedString( - [NSString stringWithUTF8String:Fl_Mac_App_Menu::show], nil) - action:@selector(unhideAllApplications:) - keyEquivalent:@""]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - // Quit AppName - title = [NSString stringWithFormat:NSLocalizedString( - [NSString stringWithUTF8String:Fl_Mac_App_Menu::quit], nil), - nsappname]; - menuItem = [appleMenu addItemWithTitle:title - action:@selector(terminate:) - keyEquivalent:@"q"]; - [menuItem setTarget:about]; - - /* Put menu into the menubar */ - menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; - [menuItem setSubmenu:appleMenu]; - mainmenu = [[NSMenu alloc] initWithTitle:@""]; - [mainmenu addItem:menuItem]; - [NSApp setMainMenu:mainmenu]; - if (services) { - [NSApp setServicesMenu:services]; - [services release]; - } - [mainmenu release]; - [appleMenu release]; - [menuItem release]; - Fl_MacOS_Sys_Menu_Bar_Driver::driver(); -} - - -void Fl_Cocoa_Window_Driver::set_key_window() -{ - [fl_xid(pWindow) makeKeyWindow]; -} - -static NSImage *imageFromText(const char *text, int *pwidth, int *pheight) -{ - const char *p, *q; - int width = 0, height, w2, ltext = (int)strlen(text); - fl_font(FL_HELVETICA, 10); - p = text; - int nl = 0; - while(nl < 100 && (q=strchr(p, '\n')) != NULL) { - nl++; - w2 = (int)fl_width(p, (int)(q - p)); - if (w2 > width) width = w2; - p = q + 1; - } - if (text[ ltext - 1] != '\n') { - nl++; - w2 = int(fl_width(p)); - if (w2 > width) width = w2; - } - height = nl * fl_height() + 3; - width += 6; - Fl_Image_Surface *off = new Fl_Image_Surface(width, height, 1); - Fl_Surface_Device::push_current(off); - CGContextSetRGBFillColor( (CGContextRef)off->offscreen(), 0,0,0,0); - fl_rectf(0,0,width,height); - fl_color(FL_BLACK); - p = text; - fl_font(FL_HELVETICA, 10); - int y = fl_height(); - while(TRUE) { - q = strchr(p, '\n'); - if (q) { - fl_draw(p, (int)(q - p), 3, y); - } else { - fl_draw(p, 3, y); - break; - } - y += fl_height(); - p = q + 1; - } - Fl_Surface_Device::pop_current(); - NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off->offscreen() ); - delete off; - *pwidth = width; - *pheight = height; - return image; -} - -static NSImage *defaultDragImage(int *pwidth, int *pheight) -{ - const int width = 50, height = 40; - Fl_Image_Surface *off = new Fl_Image_Surface(width, height, 1); - Fl_Surface_Device::push_current(off); - fl_font(FL_HELVETICA, 20); - fl_color(FL_BLACK); - char str[4]; - // the "Delivery truck" Unicode character from "Apple Color Emoji" font - int l = fl_utf8encode(0x1F69A, str); - fl_draw(str, l, 1, 16); - Fl_Surface_Device::pop_current(); - NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off->offscreen() ); - delete off; - *pwidth = width; - *pheight = height; - return image; -} - - -int Fl_Cocoa_Screen_Driver::dnd(int use_selection) -{ - CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[0], fl_selection_length[0]); - if (text==NULL) return false; - NSAutoreleasePool *localPool; - localPool = [[NSAutoreleasePool alloc] init]; - Fl_Widget *w = Fl::pushed(); - Fl_Window *win = w->top_window(); - FLView *myview = (FLView*)[fl_mac_xid(win) contentView]; - NSEvent *theEvent = [NSApp currentEvent]; - - int width, height; - NSImage *image; - if (use_selection) { - fl_selection_buffer[0][ fl_selection_length[0] ] = 0; - image = imageFromText(fl_selection_buffer[0], &width, &height); - } else { - image = defaultDragImage(&width, &height); - } - - NSPoint pt = [theEvent locationInWindow]; - pt.x -= width/2; - pt.y -= height/2; - NSPasteboardItem *pbItem = [[[NSPasteboardItem alloc] init] autorelease]; - [pbItem setData:(NSData*)text forType:UTF8_pasteboard_type]; - NSDraggingItem *dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem] autorelease]; - NSRect r = {pt, {CGFloat(width), CGFloat(height)}}; - [dragItem setDraggingFrame:r contents:image]; - [myview beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem] event:theEvent source:myview]; - CFRelease(text); - [localPool release]; - return true; -} - -// rescales an NSBitmapImageRep (and also rewrites it with integer pixels) -static NSBitmapImageRep *scale_nsbitmapimagerep(NSBitmapImageRep *img, float scale) -{ - int w = (int)[img pixelsWide]; - int h = (int)[img pixelsHigh]; - long int scaled_w = lround(scale * w); - long int scaled_h = lround(scale * h); - NSBitmapImageRep *scaled = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL - pixelsWide:scaled_w - pixelsHigh:scaled_h - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:scaled_w*4 - bitsPerPixel:32]; - NSDictionary *dict = - [NSDictionary dictionaryWithObject:scaled - forKey:NSGraphicsContextDestinationAttributeName]; - NSGraphicsContext *oldgc = [NSGraphicsContext currentContext]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithAttributes:dict]]; - [[NSColor clearColor] set]; - NSRect r = NSMakeRect(0, 0, scaled_w, scaled_h); - NSRectFill(r); - [img drawInRect:r]; - [NSGraphicsContext setCurrentContext:oldgc]; - [img release]; - return scaled; -} - -static void write_bitmap_inside(NSBitmapImageRep *to, int to_width, NSBitmapImageRep *from, - int to_x, int to_y) - /* Copies in bitmap "to" the bitmap "from" with its top-left angle at coordinates to_x, to_y - On retina displays both bitmaps have double width and height - to_width is the width in screen units of "to". On retina, its pixel width is twice that. - */ -{ - const uchar *from_data = [from bitmapData]; - // 10.4 required by the bitmapFormat message - if (([to bitmapFormat] & NSBitmapFormatAlphaFirst) && - !([from bitmapFormat] & NSBitmapFormatAlphaFirst) ) { - // "to" is ARGB and "from" is RGBA --> convert "from" to ARGB - // it is enough to read "from" starting one byte earlier, because A is always 0xFF: - // RGBARGBA becomes (A)RGBARGB - from_data--; - } else if ( !([to bitmapFormat] & NSBitmapFormatAlphaFirst) && ([from bitmapFormat] & NSBitmapFormatAlphaFirst) ) { - // "from" is ARGB and "to" is RGBA --> convert "from" to RGBA - // it is enough to offset reading by one byte because A is always 0xFF - // so ARGBARGB becomes RGBARGB(A) as needed - from_data++; - } - int to_w = (int)[to pixelsWide]; // pixel width of "to" - int from_w = (int)[from pixelsWide]; // pixel width of "from" - int from_h = (int)[from pixelsHigh]; // pixel height of "from" - int to_depth = (int)[to samplesPerPixel]; - int from_depth = (int)[from samplesPerPixel]; - int depth = 0; - if (to_depth > from_depth) depth = from_depth; - else if (from_depth > to_depth) depth = to_depth; - float factor = to_w / (float)to_width; // scaling factor is 1 for classic displays and 2 for retina - to_x = factor*to_x; // transform offset from screen unit to pixels - to_y = factor*to_y; - // perform the copy - uchar *tobytes = [to bitmapData] + to_y * to_w * to_depth + to_x * to_depth; - const uchar *frombytes = from_data; - for (int i = 0; i < from_h; i++) { - if (depth == 0) { // depth is always 0 in case of RGBA <-> ARGB conversion - if (i == 0 && from_data < [from bitmapData]) { - memcpy(tobytes+1, frombytes+1, from_w * from_depth-1); // avoid reading before [from bitmapData] - *tobytes = 0xFF; // set the very first A byte - } else if (i == from_h - 1 && from_data > [from bitmapData]) { - memcpy(tobytes, frombytes, from_w * from_depth - 1); // avoid reading after end of [from bitmapData] - *(tobytes + from_w * from_depth - 1) = 0xFF; // set the very last A byte - } else { - memcpy(tobytes, frombytes, from_w * from_depth); - } - } else { - for (int j = 0; j < from_w; j++) { - memcpy(tobytes + j * to_depth, frombytes + j * from_depth, depth); - } - } - tobytes += to_w * to_depth; - frombytes += from_w * from_depth; - } -} - - -static NSBitmapImageRep* GL_rect_to_nsbitmap(Fl_Window *win, int x, int y, int w, int h) -// captures a rectangle from a GL window and returns it as an allocated NSBitmapImageRep -// the capture has high res on retina -{ - Fl_Device_Plugin *plugin = Fl_Device_Plugin::opengl_plugin(); - if (!plugin) return nil; - Fl_RGB_Image *img = plugin->rectangle_capture(win, x, y, w, h); - NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:img->w() pixelsHigh:img->h() bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:4*img->w() bitsPerPixel:32]; - if (img->d() == 4) memcpy([bitmap bitmapData], img->array, 4*img->data_w()*img->data_h()); - else { - memset([bitmap bitmapData], 0xFF, [bitmap bytesPerPlane]); - const uchar *from = img->array; - for (int r = 0; r < img->h(); r++) { - uchar *to = [bitmap bitmapData] + r * [bitmap bytesPerRow]; - for (int c = 0; c < img->w(); c++) { - memcpy(to, from, 3); - from += 3; - to += 4; - } - } - } - delete img; - return bitmap; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -static NSBitmapImageRep* rect_to_NSBitmapImage_layer(Fl_Window *win, int x, int y, int w, int h) -{ // capture window data for layer-based views because initWithFocusedViewRect: does not work for them - FLView *view = (FLView*)[fl_xid(win) contentView]; - if (!view->aux_bitmap) return nil; - CGImageRef cgimg = CGBitmapContextCreateImage(view->aux_bitmap); - if (x || y || w != win->w() || h != win->h()) { - float s = Fl::screen_driver()->scale(0); - if (Fl_Cocoa_Window_Driver::driver(win)->mapped_to_retina()) s *= 2; - CGRect rect = CGRectMake(x * s, y * s, w * s, h * s); - CGImageRef cgimg2 = CGImageCreateWithImageInRect(cgimg, rect); - CGImageRelease(cgimg); - cgimg = cgimg2; - } - NSBitmapImageRep *bitmap = (cgimg ? [[NSBitmapImageRep alloc] initWithCGImage:cgimg/*10.5*/] : nil); - CGImageRelease(cgimg); - return bitmap; -} -#endif - -static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h) { - NSBitmapImageRep *bitmap = nil; - NSRect rect; - float s = Fl_Graphics_Driver::default_driver().scale(); - if (win->as_gl_window() && y >= 0) { - bitmap = GL_rect_to_nsbitmap(win, x, y, w, h); - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - else if (views_use_CA) { - bitmap = rect_to_NSBitmapImage_layer(win, x, y, w, h); - } -#endif - else { - NSView *winview = nil; - if ( through_Fl_X_flush && Fl_Window::current() == win ) { - rect = NSMakeRect(x - 0.5, y - 0.5, w, h); - } - else { - winview = [fl_xid(win) contentView]; - int view_h = [winview frame].size.height; - rect = NSMakeRect(int(x*s), int(view_h-y*s-int(h*s)), int(w*s), int(h*s)); - // lock focus to win's view - if (fl_mac_os_version >= 101100) { - NSGraphicsContext *ctxt = [fl_xid(win) - performSelector:@selector(graphicsContext)]; - [ctxt saveGraphicsState]; // necessary under 10.11 - } - [winview performSelector:@selector(lockFocus)]; - } - // The image depth is 3 until macOS 10.5 and 4 with 10.6 and above - // the deprecation warning at 10.14 can be ignored because runs only for macOS < 10.14 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect]; -#pragma clang diagnostic pop - if ( !( through_Fl_X_flush && Fl_Window::current() == win) ) { - [winview performSelector:@selector(unlockFocus)]; - if (fl_mac_os_version >= 101100) { - NSGraphicsContext *ctxt = [fl_xid(win) - performSelector:@selector(graphicsContext)]; - [ctxt restoreGraphicsState]; - } - } - } - return bitmap; -} - -static NSBitmapImageRep* rect_to_NSBitmapImageRep_subwins(Fl_Window *win, int x, int y, int w, int h, bool capture_subwins) -/* Captures a rectangle from a mapped window. - On retina displays, the resulting bitmap has 2 pixels per screen unit. - The returned value is to be released after use - */ -{ - Fl_Rect r(x, y, w, h); - NSBitmapImageRep *bitmap = [fl_xid(win) rect_to_NSBitmapImageRep:&r]; - if (!capture_subwins || !bitmap) return bitmap; - - // capture also subwindows - NSArray *children = [fl_xid(win) childWindows]; // 10.2 - NSEnumerator *enumerator = [children objectEnumerator]; - id child; - while ((child = [enumerator nextObject]) != nil) { - if (![child isKindOfClass:[FLWindow class]]) continue; - Fl_Window *sub = [(FLWindow*)child getFl_Window]; - CGRect rsub = CGRectMake(sub->x(), win->h() -(sub->y()+sub->h()), sub->w(), sub->h()); - CGRect clip = CGRectMake(x, win->h()-(y+h), w, h); - clip = CGRectIntersection(rsub, clip); - if (CGRectIsNull(clip)) continue; - NSBitmapImageRep *childbitmap = rect_to_NSBitmapImageRep_subwins(sub, clip.origin.x - sub->x(), - win->h() - clip.origin.y - sub->y() - clip.size.height, clip.size.width, clip.size.height, true); - if (childbitmap) { - // if bitmap is high res and childbitmap is not, childbitmap must be rescaled - if (!win->as_gl_window() && Fl_Cocoa_Window_Driver::driver(win)->mapped_to_retina() && - sub->as_gl_window() && !Fl::use_high_res_GL()) { - childbitmap = scale_nsbitmapimagerep(childbitmap, 2); - } - float s = Fl_Graphics_Driver::default_driver().scale(); - write_bitmap_inside(bitmap, w * s, childbitmap, - (clip.origin.x - x) * s, - (win->h() - clip.origin.y - clip.size.height - y) * s ); - } - [childbitmap release]; - } - return bitmap; -} - -static void nsbitmapProviderReleaseData (void *info, const void *data, size_t size) -{ - [(NSBitmapImageRep*)info release]; -} - -CGImageRef Fl_Cocoa_Window_Driver::CGImage_from_window_rect(int x, int y, int w, int h, bool capture_subwins) -{ - /* Returns a capture of a rectangle of a mapped window as a CGImage. - With retina displays, the returned image has twice the width and height. - CFRelease the returned CGImageRef after use - */ - CGImageRef img; - NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep_subwins(pWindow, x, y, w, h, capture_subwins); - img = (CGImageRef)[bitmap performSelector:@selector(CGImage)]; // requires Mac OS 10.5 - CGImageRetain(img); - [bitmap release]; - return img; -} - -int Fl_Cocoa_Window_Driver::decorated_w() -{ - if (!shown() || parent() || !border() || !visible()) - return w(); - int bx=0; - get_window_frame_sizes(pWindow, &bx); - return w() + 2 * bx; -} - -int Fl_Cocoa_Window_Driver::decorated_h() -{ - if (!shown() || parent() || !border() || !visible()) - return h(); - int bx = 0, by = 0; - int bt = get_window_frame_sizes(pWindow, &bx, &by); - float s = Fl::screen_driver()->scale(0); - return h() + bt/s; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 && defined(__BLOCKS__) - -// Requires -weak_framework ScreenCaptureKit and used by FLTK for macOS ≥ 15.0 -static CGImageRef capture_decorated_window_SCK(NSWindow *nswin) { - if (@available(macOS 15.0, *)) { - __block CGImageRef capture = NULL; - __block BOOL capture_err = NO; - void (^block_to_stop_main_loop)(void) = ^{ CFRunLoopStop(CFRunLoopGetMain()); }; - // Fix for bug in ScreenCaptureKit that modifies a window's styleMask the first time - // it captures a non-resizable window. We memorize each non-resizable window's styleMask, - // and we restore modified styleMasks later, after the screen capture. - NSMutableArray *xid_array = [NSMutableArray arrayWithCapacity:2]; - NSMutableArray *mask_array = [NSMutableArray arrayWithCapacity:2]; - Fl_Window *win = Fl::first_window(); - while (win) { - if (!win->parent() && win->border()) { - FLWindow *xid = fl_mac_xid(win); - if (xid && !([xid styleMask] & NSWindowStyleMaskResizable)) { - [xid_array addObject:xid]; - NSUInteger mask = [xid styleMask]; - [mask_array addObject:[NSData dataWithBytes:&mask length:sizeof(NSUInteger)]]; - } - } - win = Fl::next_window(win); - } - CGWindowID target_id = (CGWindowID)[nswin windowNumber]; - NSRect r = [nswin frame]; - int W = r.size.width, H = r.size.height; - [SCShareableContent getCurrentProcessShareableContentWithCompletionHandler: // macOS 14.4 - ^(SCShareableContent *shareableContent, NSError *error) { - SCWindow *scwin = nil; - if (!error) { - NSEnumerator *enumerator = [[shareableContent windows] objectEnumerator]; - while ((scwin = (SCWindow*)[enumerator nextObject]) != nil) { - if ([scwin windowID] == target_id) { - break; - } - } - } - if (!scwin) { - capture_err = YES; - dispatch_async(dispatch_get_main_queue(), block_to_stop_main_loop); - return; - } - SCContentFilter *filter = [[[SCContentFilter alloc] initWithDesktopIndependentWindow:scwin] autorelease]; - int s = (int)[filter pointPixelScale]; - SCStreamConfiguration *config = [[[SCStreamConfiguration alloc] init] autorelease]; - [config setIgnoreShadowsSingleWindow:YES]; - [config setIgnoreShadowsDisplay:YES]; // necessary with macOS 26 Tahoe - [config setShowsCursor:NO]; - [config setWidth:W*s]; - [config setHeight:H*s]; - [config setIncludeChildWindows:NO]; // macOS 14.2 - [SCScreenshotManager captureImageWithFilter:filter - configuration:config - completionHandler:^(CGImageRef sampleBuffer, NSError *error) { - if (error) capture_err = YES; - else { - capture = sampleBuffer; - CGImageRetain(capture); - } - dispatch_async(dispatch_get_main_queue(), block_to_stop_main_loop); - } - ]; - } - ]; - // run the main loop until the 1 or 2 blocks above have completed and have stopped the loop - while (!capture_err && !capture) CFRunLoopRun(); - if (capture_err) return NULL; - // ScreenCaptureKit bug cont'd: restore modified styleMasks. - for (int i = 0, count = (int)[xid_array count]; i < count; i++) { - NSUInteger mask; - [(NSData*)[mask_array objectAtIndex:i] getBytes:&mask length:sizeof(NSUInteger)]; - NSWindow *xid = (NSWindow*)[xid_array objectAtIndex:i]; - if (mask != [xid styleMask]) [xid setStyleMask:mask]; - } - return capture; - } else return NULL; -} -#endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 - - -CGImageRef Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(NSWindow *nswin) { - // usable with 10.5 and above - CGImageRef img = NULL; -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 && defined(__BLOCKS__) - if (fl_mac_os_version >= 150000) - img = capture_decorated_window_SCK(nswin); - else -#endif - { -# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_15_0 - NSInteger win_id = [nswin windowNumber]; - CFArrayRef array = CFArrayCreate(NULL, (const void**)&win_id, 1, NULL); - img = CGWindowListCreateImageFromArray(CGRectNull, array, kCGWindowImageBoundsIgnoreFraming); // 10.5 - CFRelease(array); -# endif - } - return img; -} - - -static CGImageRef capture_window_titlebar(Fl_Window *win, Fl_Cocoa_Window_Driver *cocoa_dr) { - CGImageRef img; - // verified OK from 10.6 - FLWindow *nswin = fl_xid(win); - CGImageRef img_full = Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(nswin); - int bt = [nswin frame].size.height - [[nswin contentView] frame].size.height; - int s = CGImageGetWidth(img_full) / [nswin frame].size.width; - CGRect cgr = CGRectMake(0, 0, CGImageGetWidth(img_full), bt * s); - img = CGImageCreateWithImageInRect(img_full, cgr); // 10.4 - CGImageRelease(img_full); - return img; -} - - -void Fl_Cocoa_Window_Driver::draw_titlebar_to_context(CGContextRef gc, int w, int h) -{ - FLWindow *nswin = fl_xid(pWindow); - if ([nswin canBecomeMainWindow]) [nswin makeMainWindow]; - [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:nil inMode:NSDefaultRunLoopMode dequeue:NO]; - CGImageRef img = capture_window_titlebar(pWindow, this); - if (img) { - CGContextSaveGState(gc); - CGContextDrawImage(gc, CGRectMake(0, 0, w, h), img); - CGImageRelease(img); - CGContextRestoreGState(gc); - } -} - - -/* Returns the version of the running Mac OS as an int such as 100802 for 10.8.2, - and also assigns that value to global fl_mac_os_version. - N.B.: macOS "Big Sur" 11.0 can produce 2 different values for fl_mac_os_version: - - when SDK 11.0 is used, fl_mac_os_version is set to 110000 (or bigger) - - when SDK 10.15 or earlier is used, fl_mac_os_version is set to 101600 - That is reported to facilitate life of apps that assumed majorVersion would remain equal to 10 - and used only minorVersion to determine what is the running version of macOS. - */ -int Fl_Darwin_System_Driver::calc_mac_os_version() { - if (fl_mac_os_version) return fl_mac_os_version; - int M = 0, m = 0, b = 0; - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) { - NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; - M = (int)version.majorVersion; - m = (int)version.minorVersion; - b = (int)version.patchVersion; - } - else -#endif - { - NSDictionary * sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; - const char *s = [[sv objectForKey:@"ProductVersion"] UTF8String]; - sscanf(s, "%d.%d.%d", &M, &m, &b); - } - [localPool release]; - fl_mac_os_version = M*10000 + m*100 + b; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (fl_mac_os_version >= 101400) views_use_CA = YES; -#endif - if (fl_mac_os_version < 100700) - fprintf(stderr, "Warning: FLTK expects macOS version 10.7 or higher"); - return fl_mac_os_version; -} - -/* - Note: `prefs` can be NULL! - */ -char *Fl_Darwin_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_Preferences::Root root, - const char *vendor, const char *application) -{ - static char *filename = 0L; - - // Allocate this only when we need it, but then keep it allocated. - if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX); - - switch (root&Fl_Preferences::ROOT_MASK) { - case Fl_Preferences::SYSTEM: - // This is safe, even on machines that use different languages - strcpy(filename, "/Library/Preferences"); - break; - case Fl_Preferences::USER: - { // Find the home directory, but return NULL if components were not found. - // If we ever port this to iOS: NSHomeDirectory returns tha location of the app! - const char *e = ::getenv("HOME"); - // if $HOME does not exist, try NSHomeDirectory, the Mac way. - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - if ( (e==0L) || (e[0]==0) || (::access(e, F_OK)==-1) ) { - NSString *nsHome = NSHomeDirectory(); - if (nsHome) - e = [nsHome UTF8String]; - } - // if NSHomeDirectory does not work, try getpwuid(), the Unix way. - if ( (e==0L) || (e[0]==0) || (::access(e, F_OK)==-1) ) { - struct passwd *pw = getpwuid(getuid()); - e = pw->pw_dir; - } - snprintf(filename, FL_PATH_MAX, "%s/Library/Preferences", e); - [localPool release]; - break; } - } - - // Make sure that the parameters are not NULL - if ( (vendor==0L) || (vendor[0]==0) ) - vendor = "unknown"; - if ( (application==0L) || (application[0]==0) ) - application = "unknown"; - - // Our C path names for preferences will be: - // SYSTEM: "/Library/Preferences/$vendor/$application.prefs" - // USER: "/Users/$user/Library/Preferences/$vendor/$application.prefs" - snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename), - "/%s/%s.prefs", vendor, application); - - return filename; -} - -Fl_Cocoa_Window_Driver::~Fl_Cocoa_Window_Driver() -{ - if (shape_data_) { - if (shape_data_->mask) { - CGImageRelease(shape_data_->mask); - } - delete shape_data_; - } - [icon_image release]; -} - -static NSImage* rgb_to_nsimage(const Fl_RGB_Image *rgb) { - if (!rgb) return nil; - int ld = rgb->ld(); - if (!ld) ld = rgb->data_w() * rgb->d(); - NSImage *win_icon = nil; - if (fl_mac_os_version >= 101000) { - NSBitmapImageRep *bitmap = - [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL - pixelsWide:rgb->data_w() - pixelsHigh:rgb->data_h() - bitsPerSample:8 - samplesPerPixel:rgb->d() - hasAlpha:!(rgb->d() & 1) - isPlanar:NO - colorSpaceName:(rgb->d() <= 2 ? NSDeviceWhiteColorSpace : - NSDeviceRGBColorSpace) - bitmapFormat:NSBitmapFormatAlphaNonpremultiplied - bytesPerRow:ld - bitsPerPixel:rgb->d() * 8]; // 10.4 - memcpy([bitmap bitmapData], rgb->array, rgb->data_h() * ld); - win_icon = [[NSImage alloc] initWithSize:NSMakeSize(0, 0)]; - [win_icon addRepresentation:bitmap]; - [bitmap release]; - } - return win_icon; -} - -void Fl_Cocoa_Window_Driver::icons(const Fl_RGB_Image *icons[], int count) { - [icon_image release]; - icon_image = nil; - if (count >= 1 && pWindow->border() && pWindow->label() && strlen(pWindow->label())) { - ((Fl_RGB_Image*)icons[0])->normalize(); - icon_image = rgb_to_nsimage(icons[0]); - } -} - -void Fl_Cocoa_Screen_Driver::default_icons(const Fl_RGB_Image *icons[], int count) { - [default_icon release]; - default_icon = nil; - if (count >= 1) { - default_icon = rgb_to_nsimage(icons[0]); - } -} - - -fl_uintptr_t Fl_Cocoa_Window_Driver::os_id() { - return [fl_xid(pWindow) windowNumber]; -} - - -// Deprecated in 1.4 - only for backward compatibility with 1.3 -void Fl::insertion_point_location(int x, int y, int height) { - Fl_Cocoa_Screen_Driver::insertion_point_location(x, y, height); -} -// Deprecated in 1.4 - only for backward compatibility with 1.3 -void Fl::reset_marked_text() { - Fl_Cocoa_Screen_Driver::reset_marked_text(); -} diff --git a/src/Fl_get_key_mac.cxx b/src/Fl_get_key_mac.cxx deleted file mode 100644 index b9ad86dd1..000000000 --- a/src/Fl_get_key_mac.cxx +++ /dev/null @@ -1,102 +0,0 @@ -// -// MacOS keyboard state routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Note: this file contains platform specific code and will therefore -// not be processed by doxygen (see Doxyfile.in). - -// Return the current state of a key. Keys are named by fltk symbols, -// which are actually X keysyms. So this has to translate to macOS -// symbols. - -#include -#include -#include "drivers/Darwin/Fl_Darwin_System_Driver.H" -#include "drivers/Cocoa/Fl_Cocoa_Screen_Driver.H" - -// The list of Mac OS virtual keycodes appears with OS 10.5 in -// ...../Carbon.framework/Frameworks/HIToolbox.framework/Headers/Events.h -#include - -// convert an FLTK (X) keysym to a MacOS symbol: -// This table is in numeric order by FLTK symbol order for binary search. -static const struct {unsigned short vk, fltk;} vktab[] = { - { kVK_Space, ' ' }, { kVK_ANSI_Quote, '\'' }, { kVK_ANSI_Comma, ',' }, { kVK_ANSI_Minus, '-' }, { kVK_ANSI_Period, '.' }, { kVK_ANSI_Slash, '/' }, - { kVK_ANSI_0, '0' }, { kVK_ANSI_1, '1' }, { kVK_ANSI_2, '2' }, { kVK_ANSI_3, '3' }, - { kVK_ANSI_4, '4' }, { kVK_ANSI_5, '5' }, { kVK_ANSI_6, '6' }, { kVK_ANSI_7, '7' }, - { kVK_ANSI_8, '8' }, { kVK_ANSI_9, '9' }, { kVK_ANSI_Semicolon, ';' }, { kVK_ANSI_Equal, '=' }, - { kVK_ANSI_A, 'A' }, { kVK_ANSI_B, 'B' }, { kVK_ANSI_C, 'C' }, { kVK_ANSI_D, 'D' }, - { kVK_ANSI_E, 'E' }, { kVK_ANSI_F, 'F' }, { kVK_ANSI_G, 'G' }, { kVK_ANSI_H, 'H' }, - { kVK_ANSI_I, 'I' }, { kVK_ANSI_J, 'J' }, { kVK_ANSI_K, 'K' }, { kVK_ANSI_L, 'L' }, - { kVK_ANSI_M, 'M' }, { kVK_ANSI_N, 'N' }, { kVK_ANSI_O, 'O' }, { kVK_ANSI_P, 'P' }, - { kVK_ANSI_Q, 'Q' }, { kVK_ANSI_R, 'R' }, { kVK_ANSI_S, 'S' }, { kVK_ANSI_T, 'T' }, - { kVK_ANSI_U, 'U' }, { kVK_ANSI_V, 'V' }, { kVK_ANSI_W, 'W' }, { kVK_ANSI_X, 'X' }, - { kVK_ANSI_Y, 'Y' }, { kVK_ANSI_Z, 'Z' }, - { kVK_ANSI_LeftBracket, '[' }, { kVK_ANSI_Backslash, '\\' }, { kVK_ANSI_RightBracket, ']' }, { kVK_ANSI_Grave, '`' }, - { kVK_VolumeDown, FL_Volume_Down}, { kVK_Mute, FL_Volume_Mute}, { kVK_VolumeUp, FL_Volume_Up}, - { kVK_Delete, FL_BackSpace }, { kVK_Tab, FL_Tab }, { kVK_ISO_Section, FL_Iso_Key }, { kVK_Return, FL_Enter }, /*{ 0x7F, FL_Pause }, - { 0x7F, FL_Scroll_Lock },*/ { kVK_Escape, FL_Escape }, - { kVK_JIS_Kana, FL_Kana}, { kVK_JIS_Eisu, FL_Eisu}, { kVK_JIS_Yen, FL_Yen}, { kVK_JIS_Underscore, FL_JIS_Underscore}, - { kVK_Home, FL_Home }, { kVK_LeftArrow, FL_Left }, - { kVK_UpArrow, FL_Up }, { kVK_RightArrow, FL_Right }, { kVK_DownArrow, FL_Down }, { kVK_PageUp, FL_Page_Up }, - { kVK_PageDown, FL_Page_Down }, { kVK_End, FL_End }, /*{ 0x7F, FL_Print }, { 0x7F, FL_Insert },*/ - { 0x6e, FL_Menu }, { kVK_Help, FL_Help }, { kVK_ANSI_KeypadClear, FL_Num_Lock }, - { kVK_ANSI_KeypadEnter, FL_KP_Enter }, { kVK_ANSI_KeypadMultiply, FL_KP+'*' }, { kVK_ANSI_KeypadPlus, FL_KP+'+'}, - { kVK_JIS_KeypadComma, FL_KP+',' }, - { kVK_ANSI_KeypadMinus, FL_KP+'-' }, { kVK_ANSI_KeypadDecimal, FL_KP+'.' }, { kVK_ANSI_KeypadDivide, FL_KP+'/' }, - { kVK_ANSI_Keypad0, FL_KP+'0' }, { kVK_ANSI_Keypad1, FL_KP+'1' }, { kVK_ANSI_Keypad2, FL_KP+'2' }, { kVK_ANSI_Keypad3, FL_KP+'3' }, - { kVK_ANSI_Keypad4, FL_KP+'4' }, { kVK_ANSI_Keypad5, FL_KP+'5' }, { kVK_ANSI_Keypad6, FL_KP+'6' }, { kVK_ANSI_Keypad7, FL_KP+'7' }, - { kVK_ANSI_Keypad8, FL_KP+'8' }, { kVK_ANSI_Keypad9, FL_KP+'9' }, { kVK_ANSI_KeypadEquals, FL_KP+'=' }, - { kVK_F1, FL_F+1 }, { kVK_F2, FL_F+2 }, { kVK_F3, FL_F+3 }, { kVK_F4, FL_F+4 }, - { kVK_F5, FL_F+5 }, { kVK_F6, FL_F+6 }, { kVK_F7, FL_F+7 }, { kVK_F8, FL_F+8 }, - { kVK_F9, FL_F+9 }, { kVK_F10, FL_F+10 }, { kVK_F11, FL_F+11 }, { kVK_F12, FL_F+12 }, - { kVK_F13, FL_F+13 }, { kVK_F14, FL_F+14 }, { kVK_F15, FL_F+15 }, { kVK_F16, FL_F+16 }, - { kVK_F17, FL_F+17 }, { kVK_F18, FL_F+18 }, { kVK_F19, FL_F+19 }, { kVK_F20, FL_F+20 }, - { kVK_Shift, FL_Shift_L }, { kVK_RightShift, FL_Shift_R }, { kVK_Control, FL_Control_L }, { kVK_RightControl, FL_Control_R }, - { kVK_CapsLock, FL_Caps_Lock }, { kVK_Command, FL_Meta_L }, { 0x36, FL_Meta_R }, - { kVK_Option, FL_Alt_L }, { kVK_RightOption, FL_Alt_R }, { kVK_ForwardDelete, FL_Delete } -}; - -// Computes the macKeyLookUp table that transforms a Mac OS virtual keycode into an FLTK keysym -unsigned short *Fl_Darwin_System_Driver::compute_macKeyLookUp() -{ - static unsigned short macKeyLookUp[128]; - memset(macKeyLookUp, 0, sizeof(macKeyLookUp)); - for (unsigned i = 0; i < sizeof(vktab)/sizeof(*vktab); i++) { - macKeyLookUp[vktab[i].vk] = vktab[i].fltk; - } - return macKeyLookUp; -} - -static int fltk2mac(int fltk) { - int a = 0; - int b = sizeof(vktab)/sizeof(*vktab); - while (a < b) { - int c = (a+b)/2; - if (vktab[c].fltk == fltk) return vktab[c].vk; - if (vktab[c].fltk < fltk) a = c+1; else b = c; - } - return vktab[a].vk; -} - -//: returns true, if that key was pressed during the last event -int Fl_Cocoa_Screen_Driver::event_key(int k) { - return get_key(k); -} - -//: returns true, if that key is pressed right now -int Fl_Cocoa_Screen_Driver::get_key(int k) { - return (int)CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, fltk2mac(k) ); // 10.4 -} diff --git a/src/Fl_get_key_win32.cxx b/src/Fl_get_key_win32.cxx deleted file mode 100644 index cb844722d..000000000 --- a/src/Fl_get_key_win32.cxx +++ /dev/null @@ -1,127 +0,0 @@ -// -// Windows keyboard state routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Note: this file contains platform specific code and will therefore -// not be processed by doxygen (see Doxyfile.in). - -// Return the current state of a key. Keys are named by fltk symbols, -// which are actually X keysyms. So this has to translate to Windows -// VK_x symbols. - -#include "drivers/WinAPI/Fl_WinAPI_Screen_Driver.H" -#include - -// Convert an FLTK (X) keysym to a Windows VK symbol: -// See also the inverse converter in Fl_win32.cxx -// This table is in numeric order by FLTK symbol order for binary search: - -static const struct {unsigned short vk, fltk;} vktab[] = { - {VK_SPACE, ' '}, - {'1', '!'}, - {0xde, '\"'}, - {'3', '#'}, - {'4', '$'}, - {'5', '%'}, - {'7', '&'}, - {0xde, '\''}, - {'9', '('}, - {'0', ')'}, - {'8', '*'}, - {0xbb, '+'}, - {0xbc, ','}, - {0xbd, '-'}, - {0xbe, '.'}, - {0xbf, '/'}, - {0xba, ':'}, - {0xba, ';'}, - {0xbc, '<'}, - {0xbb, '='}, - {0xbe, '>'}, - {0xbf, '?'}, - {'2', '@'}, - {0xdb, '['}, - {0xdc, '\\'}, - {0xdd, ']'}, - {'6', '^'}, - {0xbd, '_'}, - {0xc0, '`'}, - {0xdb, '{'}, - {0xdc, '|'}, - {0xdd, '}'}, - {0xc0, '~'}, - {VK_BACK, FL_BackSpace}, - {VK_TAB, FL_Tab}, - {VK_CLEAR, 0xff0b/*XK_Clear*/}, - {0xe2 /*VK_OEM_102*/, FL_Iso_Key}, - {VK_RETURN, FL_Enter}, - {VK_PAUSE, FL_Pause}, - {VK_SCROLL, FL_Scroll_Lock}, - {VK_ESCAPE, FL_Escape}, - {VK_HOME, FL_Home}, - {VK_LEFT, FL_Left}, - {VK_UP, FL_Up}, - {VK_RIGHT, FL_Right}, - {VK_DOWN, FL_Down}, - {VK_PRIOR, FL_Page_Up}, - {VK_NEXT, FL_Page_Down}, - {VK_END, FL_End}, - {VK_SNAPSHOT, FL_Print}, - {VK_INSERT, FL_Insert}, - {VK_APPS, FL_Menu}, - {VK_NUMLOCK, FL_Num_Lock}, -//{VK_???, FL_KP_Enter}, - {VK_MULTIPLY, FL_KP+'*'}, - {VK_ADD, FL_KP+'+'}, - {VK_SUBTRACT, FL_KP+'-'}, - {VK_DECIMAL, FL_KP+'.'}, - {VK_DIVIDE, FL_KP+'/'}, - {VK_LSHIFT, FL_Shift_L}, - {VK_RSHIFT, FL_Shift_R}, - {VK_LCONTROL, FL_Control_L}, - {VK_RCONTROL, FL_Control_R}, - {VK_CAPITAL, FL_Caps_Lock}, - {VK_LWIN, FL_Meta_L}, - {VK_RWIN, FL_Meta_R}, - {VK_LMENU, FL_Alt_L}, - {VK_RMENU, FL_Alt_R}, - {VK_DELETE, FL_Delete} -}; - -static int fltk2ms(int fltk) { - if (fltk >= '0' && fltk <= '9') return fltk; - if (fltk >= 'A' && fltk <= 'Z') return fltk; - if (fltk >= 'a' && fltk <= 'z') return fltk-('a'-'A'); - if (fltk > FL_F && fltk <= FL_F+16) return fltk-(FL_F-(VK_F1-1)); - if (fltk >= FL_KP+'0' && fltk<=FL_KP+'9') return fltk-(FL_KP+'0'-VK_NUMPAD0); - int a = 0; - int b = sizeof(vktab)/sizeof(*vktab); - while (a < b) { - int c = (a+b)/2; - if (vktab[c].fltk == fltk) return vktab[c].vk; - if (vktab[c].fltk < fltk) a = c+1; else b = c; - } - return 0; -} - -int Fl_WinAPI_Screen_Driver::event_key(int k) { - return GetKeyState(fltk2ms(k))&~1; -} - -int Fl_WinAPI_Screen_Driver::get_key(int k) { - uchar foo[256]; - GetKeyboardState(foo); - return foo[fltk2ms(k)]&~1; -} diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx deleted file mode 100644 index b5aca1c78..000000000 --- a/src/Fl_win32.cxx +++ /dev/null @@ -1,2976 +0,0 @@ -// -// Windows-specific code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Note: this file contains platform specific code and will therefore -// not be processed by doxygen (see Doxyfile.in). - -// This file contains Windows-specific code for FLTK which is always linked -// in. Search other files for "_WIN32" or filenames ending in _win32.cxx -// for other system-specific code. - -/* We require Windows 2000 features (e.g. VK definitions) */ -# if !defined(WINVER) || (WINVER < 0x0500) -# ifdef WINVER -# undef WINVER -# endif -# define WINVER 0x0500 -# endif -# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# ifdef _WIN32_WINNT -# undef _WIN32_WINNT -# endif -# define _WIN32_WINNT 0x0500 -# endif - -// recent versions of MinGW warn: "Please include winsock2.h before windows.h" -#if !defined(__CYGWIN__) -# include -#endif -#include -#include -#include -// Some versions of MinGW now require us to explicitly include winerror to get S_OK defined -#include -#include // for ceil() and round() -#include // for min and max (clamp is C++17) - -void fl_free_fonts(void); -void fl_release_dc(HWND, HDC); -void fl_cleanup_dc_list(void); - -#include -#include -#include -#include "Fl_Window_Driver.H" -#include "Fl_Screen_Driver.H" -#include "Fl_Timeout.h" -#include "print_button.h" -#include // for fl_graphics_driver -#include "drivers/WinAPI/Fl_WinAPI_Window_Driver.H" -#include "drivers/WinAPI/Fl_WinAPI_System_Driver.H" -#include "drivers/WinAPI/Fl_WinAPI_Screen_Driver.H" -#include "drivers/GDI/Fl_GDI_Graphics_Driver.H" -#include -#include -#include -#include -#include -#include -#include -#include -#include "flstring.h" -#include "drivers/GDI/Fl_Font.H" -#include -#include -#include -#include -#include -#ifdef __CYGWIN__ -# include -# include -#endif - -#if !defined(NO_TRACK_MOUSE) -# include // TrackMouseEvent -#endif - -#if defined(__GNUC__) -# include -#endif - -// old versions of MinGW lack definition of GET_XBUTTON_WPARAM: - -#ifndef GET_XBUTTON_WPARAM -#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) -#endif - -static bool is_dpi_aware = false; - -extern bool fl_clipboard_notify_empty(void); -extern void fl_trigger_clipboard_notify(int source); -extern HBRUSH fl_brush_action(int action); -extern void fl_cleanup_pens(void); - -// MSVC 2010 can't find round() although is included above, -// which is surprising because ceil() works fine. -// We could (should?) probably add CMake feature tests for round() -// and ceil() rather than depending on MSVC version numbers. -// AlbrechtS, 02/2010 - Note: we don't know about MSVC 2012 - 2015, see -// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros - -#if defined(_MSC_VER) && _MSC_VER <= 1600 -#define round(A) int((A) + 0.5) -#endif // _MSC_VER <= 1600 - -// Internal functions -static void fl_clipboard_notify_target(HWND wnd); -static void fl_clipboard_notify_untarget(HWND wnd); -static int clamp(int v, int a, int b) { - if (v < a) return a; - if (v > b) return b; - return v; -} - -// Internal variables -static HWND clipboard_wnd = 0; -static HWND next_clipboard_wnd = 0; - -static bool initial_clipboard = true; - -// dynamic wsock dll handling api: -#if defined(__CYGWIN__) && !defined(SOCKET) -# define SOCKET int -#endif - -/* - Dynamic linking of imm32.dll - This library is only needed for a hand full (four ATM) functions relating to - international text rendering and locales. Dynamically loading reduces initial - size and link dependencies. -*/ -static HMODULE s_imm_module = 0; -typedef BOOL(WINAPI *flTypeImmAssociateContextEx)(HWND, HIMC, DWORD); -static flTypeImmAssociateContextEx flImmAssociateContextEx = 0; -typedef HIMC(WINAPI *flTypeImmGetContext)(HWND); -static flTypeImmGetContext flImmGetContext = 0; -typedef BOOL(WINAPI *flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM); -static flTypeImmSetCompositionWindow flImmSetCompositionWindow = 0; -typedef BOOL(WINAPI *flTypeImmReleaseContext)(HWND, HIMC); -static flTypeImmReleaseContext flImmReleaseContext = 0; - -static void get_imm_module() { - s_imm_module = LoadLibrary("IMM32.DLL"); - if (!s_imm_module) - Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n" - "Please check your input method manager library accessibility."); - flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx"); - flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext"); - flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow"); - flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext"); -} - -// USE_TRACK_MOUSE - define NO_TRACK_MOUSE if you don't have -// TrackMouseEvent()... -// -// Now (Dec. 2008) we can assume that current Cygwin/MinGW versions -// support the TrackMouseEvent() function, but WinCE obviously doesn't -// support it (STR 2095). Therefore, USE_TRACK_MOUSE is enabled by -// default, but you can disable it by defining NO_TRACK_MOUSE. -// -// TrackMouseEvent is only used to support window leave notifications -// under Windows. It can be used to get FL_LEAVE events, when the -// mouse leaves the _main_ application window (FLTK detects subwindow -// leave events by using normal move events). -// -// Implementation note: If the mouse cursor leaves one subwindow and -// enters another window, then Windows sends a WM_MOUSEMOVE message to -// the new window before it sends a WM_MOUSELEAVE message to the old -// (just left) window. We save the current mouse window in a static variable, -// and if we get a WM_MOUSELEAVE event for the current mouse window, this -// means that the top level window has been left (otherwise we would have -// got another WM_MOUSEMOVE message before). - -// #define NO_TRACK_MOUSE - -#if !defined(NO_TRACK_MOUSE) -# define USE_TRACK_MOUSE -#endif // NO_TRACK_MOUSE - -static Fl_Window *track_mouse_win = 0; // current TrackMouseEvent() window - -// USE_CAPTURE_MOUSE_WIN - this must be defined for TrackMouseEvent to work -// correctly with subwindows - otherwise a single mouse click and release -// (without a move) would generate phantom leave events. -// This defines, if the current mouse window (maybe a subwindow) or the -// main window should get mouse events after pushing (and holding) a mouse -// button, i.e. when dragging the mouse. This is done by calling SetCapture -// (see below). - -#ifdef USE_TRACK_MOUSE -#define USE_CAPTURE_MOUSE_WIN -#endif // USE_TRACK_MOUSE - -// -// WM_SYNCPAINT is an "undocumented" message, which is finally defined in -// VC++ 6.0. -// - -#ifndef WM_SYNCPAINT -# define WM_SYNCPAINT 0x0088 -#endif - -#ifndef WM_MOUSELEAVE -# define WM_MOUSELEAVE 0x02a3 -#endif - -#ifndef WM_MOUSEWHEEL -# define WM_MOUSEWHEEL 0x020a -#endif - -#ifndef WHEEL_DELTA -# define WHEEL_DELTA 120 // according to MSDN. -#endif - -// This is only defined on Vista and upwards... -#ifndef WM_MOUSEHWHEEL -# define WM_MOUSEHWHEEL 0x020E -#endif - -#ifndef SM_CXPADDEDBORDER -# define SM_CXPADDEDBORDER (92) // STR #3061 -#endif - -// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx -#ifndef WM_DPICHANGED -# define WM_DPICHANGED 0x02E0 -#endif - -// -// WM_FLSELECT is the user-defined message that we get when one of -// the sockets has pending data, etc. -// - -#define WM_FLSELECT (WM_APP + 1) // WM_APP is used for hide-window - - -//////////////////////////////////////////////////////////////// -// interface to poll/select call: - -// fd's are only implemented for sockets. Microsoft Windows does not -// have a unified IO system, so it doesn't support select() on files, -// devices, or pipes... -// -// Microsoft provides the Berkeley select() call and an asynchronous -// select function that sends a Windows message when the select condition -// exists. However, we don't try to use the asynchronous WSAAsyncSelect() -// any more for good reasons (see above). -// -// A.S. Dec 2009: We got reports that current winsock2.h files define -// POLLIN, POLLOUT, and POLLERR with conflicting values WRT what we -// used before (STR #2301). Therefore we must not use these values -// for our internal purposes, but use FL_READ, FL_WRITE, and -// FL_EXCEPT, as defined for use in Fl::add_fd(). -// -static int maxfd = 0; -static fd_set fdsets[3]; - -extern IDropTarget *flIDropTarget; - -static int nfds = 0; -static int fd_array_size = 0; -static struct FD { - int fd; - short events; - void (*cb)(FL_SOCKET, void *); // keep socket api opaque at this level to reduce multiplatform deps headaches - void *arg; -} *fd = 0; - -extern unsigned int fl_codepage; - -void Fl_WinAPI_System_Driver::add_fd(int n, int events, void (*cb)(FL_SOCKET, void *), void *v) { - remove_fd(n, events); - int i = nfds++; - if (i >= fd_array_size) { - fd_array_size = 2 * fd_array_size + 1; - fd = (FD *)realloc(fd, fd_array_size * sizeof(FD)); - } - fd[i].fd = n; - fd[i].events = (short)events; - fd[i].cb = cb; - fd[i].arg = v; - - if (events & FL_READ) - FD_SET((unsigned)n, &fdsets[0]); - if (events & FL_WRITE) - FD_SET((unsigned)n, &fdsets[1]); - if (events & FL_EXCEPT) - FD_SET((unsigned)n, &fdsets[2]); - if (n > maxfd) - maxfd = n; -} - -void Fl_WinAPI_System_Driver::add_fd(int fd, void (*cb)(FL_SOCKET, void *), void *v) { - add_fd(fd, FL_READ, cb, v); -} - -void Fl_WinAPI_System_Driver::remove_fd(int n, int events) { - int i, j; - for (i = j = 0; i < nfds; i++) { - if (fd[i].fd == n) { - short e = fd[i].events & ~events; - if (!e) - continue; // if no events left, delete this fd - fd[i].events = e; - } - // move it down in the array if necessary: - if (j < i) { - fd[j] = fd[i]; - } - j++; - } - nfds = j; - - if (events & FL_READ) - FD_CLR(unsigned(n), &fdsets[0]); - if (events & FL_WRITE) - FD_CLR(unsigned(n), &fdsets[1]); - if (events & FL_EXCEPT) - FD_CLR(unsigned(n), &fdsets[2]); -} - -void Fl_WinAPI_System_Driver::remove_fd(int n) { - remove_fd(n, -1); -} - -// these pointers are set by the Fl::lock() function: -static void nothing() {} -void (*fl_lock_function)() = nothing; -void (*fl_unlock_function)() = nothing; - -static void *thread_message_; -void *Fl_WinAPI_System_Driver::thread_message() { - void *r = thread_message_; - thread_message_ = 0; - return r; -} - -extern int fl_send_system_handlers(void *e); - -MSG fl_msg; - -// A local helper function to flush any pending callback requests -// from the awake ring-buffer -static void process_awake_handler_requests(void) { - Fl_Awake_Handler func; - void *data; - while (Fl_WinAPI_System_Driver::pop_awake_handler(func, data) == 0) { - func(data); - } -} - -// This is never called with time_to_wait < 0.0. -// It *should* return negative on error, 0 if nothing happens before -// timeout, and >0 if any callbacks were done. This version -// always returns 1. -double Fl_WinAPI_System_Driver::wait(double time_to_wait) { - - time_to_wait = Fl_System_Driver::wait(time_to_wait); - - int have_message = 0; - - if (nfds) { - // For Windows we need to poll for socket input FIRST, since - // the event queue is not something we can select() on... - timeval t; - t.tv_sec = 0; - t.tv_usec = 0; - - fd_set fdt[3]; - memcpy(fdt, fdsets, sizeof fdt); // one shot faster fdt init - if (select(maxfd + 1, &fdt[0], &fdt[1], &fdt[2], &t)) { - // We got something - do the callback! - for (int i = 0; i < nfds; i++) { - SOCKET f = fd[i].fd; - short revents = 0; - if (FD_ISSET(f, &fdt[0])) - revents |= FL_READ; - if (FD_ISSET(f, &fdt[1])) - revents |= FL_WRITE; - if (FD_ISSET(f, &fdt[2])) - revents |= FL_EXCEPT; - if (fd[i].events & revents) - fd[i].cb(f, fd[i].arg); - } - time_to_wait = 0.0; // just peek for any messages - } else { - // we need to check them periodically, so set a short timeout: - if (time_to_wait > .001) - time_to_wait = .001; - } - } - - if (Fl::idle() || Fl::damage()) - time_to_wait = 0.0; - - // if there are no more windows and this timer is set - // to FOREVER, continue through or look up indefinitely - if (!Fl::first_window() && time_to_wait == 1e20) - time_to_wait = 0.0; - - fl_unlock_function(); - - time_to_wait = (time_to_wait > 10000 ? 10000 : time_to_wait); - - time_to_wait = Fl_Timeout::time_to_wait(time_to_wait); - - int t_msec = (int)(time_to_wait * 1000.0 + 0.5); - MsgWaitForMultipleObjects(0, NULL, FALSE, t_msec, QS_ALLINPUT); - - fl_lock_function(); - - // Execute the message we got, and all other pending messages: - // have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE); - while ((have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE)) > 0) { - if (fl_send_system_handlers(&fl_msg)) - continue; - - // Let applications treat WM_QUIT identical to SIGTERM on *nix - if (fl_msg.message == WM_QUIT) - raise(SIGTERM); - - if (fl_msg.message == fl_wake_msg) { - // Used for awaking wait() from another thread - thread_message_ = (void *)fl_msg.wParam; - process_awake_handler_requests(); - } - - TranslateMessage(&fl_msg); - DispatchMessageW(&fl_msg); - } - - // The following conditional test: !Fl_System_Driver::awake_ring_empty() - // equivalent to: - // (Fl::awake_ring_head_ != Fl::awake_ring_tail_) - // is a workaround / fix for STR #3143. This works, but a better solution - // would be to understand why the PostThreadMessage() messages are not - // seen by the main window if it is being dragged/ resized at the time. - // If a worker thread posts an awake callback to the ring buffer - // whilst the main window is unresponsive (if a drag or resize operation - // is in progress) we may miss the PostThreadMessage(). So here, we check if - // there is anything pending in the awake ring buffer and if so process - // it. This is not strictly thread safe (for speed it compares the head - // and tail indices without first locking the ring buffer) but is intended - // only as a fall-back recovery mechanism if the awake processing stalls. - // If the test erroneously returns true (may happen if we test the indices - // whilst they are being modified) we will call process_awake_handler_requests() - // unnecessarily, but this has no harmful consequences so is safe to do. - // Note also that if we miss the PostThreadMessage(), then thread_message_ - // will not be updated, so this is not a perfect solution, but it does - // recover and process any pending awake callbacks. - // Normally the ring buffer head and tail indices will match and this - // comparison will do nothing. Addresses STR #3143 - if (!Fl_System_Driver::awake_ring_empty()) { - process_awake_handler_requests(); - } - - Fl::flush(); - - // This should return 0 if only timer events were handled: - return 1; -} - -// just like Fl_WinAPI_System_Driver::wait(0.0) except no callbacks are done: -int Fl_WinAPI_System_Driver::ready() { - if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) - return 1; - if (!nfds) - return 0; - timeval t; - t.tv_sec = 0; - t.tv_usec = 0; - fd_set fdt[3]; - memcpy(fdt, fdsets, sizeof fdt); - return select(0, &fdt[0], &fdt[1], &fdt[2], &t); -} - -static void delayed_create_print_window(void *) { - Fl::remove_check(delayed_create_print_window); - fl_create_print_window(); -} - -void Fl_WinAPI_Screen_Driver::open_display_platform() { - static char beenHereDoneThat = 0; - - if (beenHereDoneThat) - return; - - beenHereDoneThat = 1; - // test whether DpiAwareness has been set before via a manifest - /*enum PROCESS_DPI_AWARENESS { // in shellscalingapi.h from Window 8.1 - PROCESS_DPI_UNAWARE, - PROCESS_SYSTEM_DPI_AWARE, - PROCESS_PER_MONITOR_DPI_AWARE - };*/ - typedef HRESULT(WINAPI * GetProcessDpiAwareness_type)(HANDLE, int *); - GetProcessDpiAwareness_type fl_GetProcessDpiAwareness = - (GetProcessDpiAwareness_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "GetProcessDpiAwareness"); - int awareness; - if (!fl_GetProcessDpiAwareness || fl_GetProcessDpiAwareness(NULL, &awareness) != S_OK) { - awareness = 0; //corresponds to PROCESS_DPI_UNAWARE; - } - if (awareness == 2 /*PROCESS_PER_MONITOR_DPI_AWARE*/) is_dpi_aware = true; - if (awareness == 0 /*PROCESS_DPI_UNAWARE*/) { // DpiAwareness has not been set via a manifest - typedef void *fl_DPI_AWARENESS_CONTEXT; - typedef BOOL(WINAPI * SetProcessDpiAwarenessContext_type)(fl_DPI_AWARENESS_CONTEXT); - SetProcessDpiAwarenessContext_type fl_SetProcessDpiAwarenessContext = - (SetProcessDpiAwarenessContext_type)GetProcAddress(LoadLibrary("User32.DLL"), "SetProcessDpiAwarenessContext"); - if (fl_SetProcessDpiAwarenessContext) { - const fl_DPI_AWARENESS_CONTEXT fl_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (fl_DPI_AWARENESS_CONTEXT)(-4); - is_dpi_aware = fl_SetProcessDpiAwarenessContext(fl_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - } - if (!is_dpi_aware) { - typedef HRESULT(WINAPI * SetProcessDpiAwareness_type)(int); - SetProcessDpiAwareness_type fl_SetProcessDpiAwareness = - (SetProcessDpiAwareness_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "SetProcessDpiAwareness"); - if (fl_SetProcessDpiAwareness) { - const int fl_PROCESS_PER_MONITOR_DPI_AWARE = 2; - if (fl_SetProcessDpiAwareness(fl_PROCESS_PER_MONITOR_DPI_AWARE) == S_OK) is_dpi_aware = true; - } - } - } - OleInitialize(0L); - get_imm_module(); - Fl::add_check(delayed_create_print_window); -} - - -void Fl_WinAPI_Screen_Driver::update_scaling_capability() { - scaling_capability = SYSTEMWIDE_APP_SCALING; - for (int ns = 1; ns < screen_count(); ns++) { - if (scale(ns) != scale(0)) { - scaling_capability = PER_SCREEN_APP_SCALING; - break; - } - } -} - -void Fl_WinAPI_Screen_Driver::desktop_scale_factor() { - typedef HRESULT(WINAPI * GetDpiForMonitor_type)(HMONITOR, int, UINT *, UINT *); - typedef HMONITOR(WINAPI * MonitorFromRect_type)(LPCRECT, DWORD); - GetDpiForMonitor_type fl_GetDpiForMonitor = NULL; - MonitorFromRect_type fl_MonitorFromRect = NULL; - if (is_dpi_aware) { - fl_GetDpiForMonitor = (GetDpiForMonitor_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "GetDpiForMonitor"); - if (fl_GetDpiForMonitor) - fl_MonitorFromRect = (MonitorFromRect_type)GetProcAddress(LoadLibrary("User32.DLL"), "MonitorFromRect"); - } - for (int ns = 0; ns < screen_count(); ns++) { - UINT dpiX, dpiY; - HRESULT r = E_INVALIDARG; - if (fl_GetDpiForMonitor && fl_MonitorFromRect) { - HMONITOR hm = fl_MonitorFromRect(&screens[ns], MONITOR_DEFAULTTONEAREST); - r = fl_GetDpiForMonitor(hm, 0, &dpiX, &dpiY); - } - if (r != S_OK) { dpiX = dpiY = 96; } - dpi[ns][0] = float(dpiX); - dpi[ns][1] = float(dpiY); - scale(ns, dpiX / 96.f); - // fprintf(LOG, "desktop_scale_factor ns=%d factor=%.2f dpi=%.1f\n", ns, scale(ns), dpi[ns][0]); - } - update_scaling_capability(); -} - - -class Fl_Win32_At_Exit { -public: - Fl_Win32_At_Exit() {} - ~Fl_Win32_At_Exit() { - fl_free_fonts(); // do some Windows cleanup - fl_cleanup_pens(); - OleUninitialize(); - if (fl_graphics_driver) fl_brush_action(1); - fl_cleanup_dc_list(); - // This is actually too late in the cleanup process to remove the - // clipboard notifications, but we have no earlier hook so we try - // to work around it anyway. - if (clipboard_wnd != NULL) - fl_clipboard_notify_untarget(clipboard_wnd); -#if USE_GDIPLUS - Fl_GDIplus_Graphics_Driver::shutdown(); -#endif - } -}; -static Fl_Win32_At_Exit win32_at_exit; - -static char im_enabled = 1; - -void Fl_WinAPI_Screen_Driver::enable_im() { - open_display(); - - Fl_X *i = Fl_X::first; - while (i) { - flImmAssociateContextEx((HWND)i->xid, 0, IACE_DEFAULT); - i = i->next; - } - - im_enabled = 1; -} - -void Fl_WinAPI_Screen_Driver::disable_im() { - open_display(); - - Fl_X *i = Fl_X::first; - while (i) { - flImmAssociateContextEx((HWND)i->xid, 0, 0); - i = i->next; - } - - im_enabled = 0; -} - -void Fl_WinAPI_Screen_Driver::set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) -{ - if (!win) return; - Fl_Window* tw = win->top_window(); - - if (!tw->shown()) - return; - - HIMC himc = flImmGetContext(fl_xid(tw)); - - if (himc) { - COMPOSITIONFORM cfs; - float s = Fl_Graphics_Driver::default_driver().scale(); - cfs.dwStyle = CFS_POINT; - cfs.ptCurrentPos.x = int(X * s); - cfs.ptCurrentPos.y = int(Y * s) - int(tw->labelsize() * s); - // Attempt to have temporary text entered by input method use scaled font. - // Does good, but still not always effective. - Fl_GDI_Font_Descriptor *desc = (Fl_GDI_Font_Descriptor*)Fl_Graphics_Driver::default_driver().font_descriptor(); - if (desc) SelectObject((HDC)Fl_Graphics_Driver::default_driver().gc(), desc->fid); - MapWindowPoints(fl_xid(win), fl_xid(tw), &cfs.ptCurrentPos, 1); - flImmSetCompositionWindow(himc, &cfs); - flImmReleaseContext(fl_xid(tw), himc); - } -} - - -//////////////////////////////////////////////////////////////// - -int Fl_WinAPI_Screen_Driver::get_mouse_unscaled(int &mx, int &my) { - POINT p; - GetCursorPos(&p); - mx = p.x; - my = p.y; - int screen = screen_num_unscaled(mx, my); - return screen >= 0 ? screen : 0; -} - -int Fl_WinAPI_Screen_Driver::get_mouse(int &x, int &y) { - int n = get_mouse_unscaled(x, y); - float s = scale(n); - x = int(x / s); - y = int(y / s); - return n; -} - -//////////////////////////////////////////////////////////////// -// code used for selections: - -char *fl_selection_buffer[2]; -int fl_selection_length[2]; -int fl_selection_buffer_length[2]; -char fl_i_own_selection[2]; - -UINT fl_get_lcid_codepage(LCID id) { - char buf[8]; - buf[GetLocaleInfo(id, LOCALE_IDEFAULTANSICODEPAGE, buf, 8)] = 0; - return atol(buf); -} - -// Convert \n -> \r\n -class Lf2CrlfConvert { - char *out; - int outlen; - -public: - Lf2CrlfConvert(const char *in, int inlen) { - outlen = 0; - const char *i; - char *o; - int lencount; - // Predict size of \r\n conversion buffer - for (i = in, lencount = inlen; lencount > 0; lencount--) { - if (*i == '\r' && *(i + 1) == '\n' && lencount >= 2) { // leave \r\n untranslated - i += 2; - outlen += 2; - lencount--; - } else if (*i == '\n') { // \n by itself? leave room to insert \r - i++; - outlen += 2; - } else { - ++i; - ++outlen; - } - } - // Alloc conversion buffer + NULL - out = new char[outlen + 1]; - // Handle \n -> \r\n conversion - for (i = in, o = out, lencount = inlen; lencount > 0; lencount--) { - if (*i == '\r' && *(i + 1) == '\n' && lencount >= 2) { // leave \r\n untranslated - *o++ = *i++; - *o++ = *i++; - lencount--; - } else if (*i == '\n') { // \n by itself? insert \r - *o++ = '\r'; - *o++ = *i++; - } else { - *o++ = *i++; - } - } - *o++ = 0; - } - ~Lf2CrlfConvert() { - delete[] out; - } - int GetLength() const { return (outlen); } - const char *GetValue() const { return (out); } -}; - -void fl_update_clipboard(void) { - Fl_Window *w1 = Fl::first_window(); - if (!w1) - return; - - HWND hwnd = fl_xid(w1); - - if (!OpenClipboard(hwnd)) - return; - - EmptyClipboard(); - - int utf16_len = fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], 0, 0); - - HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc. - LPVOID memLock = GlobalLock(hMem); - - fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], (unsigned short *)memLock, utf16_len + 1); - - GlobalUnlock(hMem); - SetClipboardData(CF_UNICODETEXT, hMem); - - CloseClipboard(); - - // In case Windows managed to lob of a WM_DESTROYCLIPBOARD during - // the above. - fl_i_own_selection[1] = 1; -} - -// call this when you create a selection: -void Fl_WinAPI_Screen_Driver::copy(const char *stuff, int len, int clipboard, const char *type) { - if (!stuff || len < 0) - return; - if (clipboard >= 2) - clipboard = 1; // Only on X11 do multiple clipboards make sense. - - // Convert \n -> \r\n (for old apps like Notepad, DOS) - Lf2CrlfConvert buf(stuff, len); - len = buf.GetLength(); - stuff = buf.GetValue(); - - if (len + 1 > fl_selection_buffer_length[clipboard]) { - delete[] fl_selection_buffer[clipboard]; - fl_selection_buffer[clipboard] = new char[len + 100]; - fl_selection_buffer_length[clipboard] = len + 100; - } - memcpy(fl_selection_buffer[clipboard], stuff, len); - fl_selection_buffer[clipboard][len] = 0; // needed for direct paste - fl_selection_length[clipboard] = len; - fl_i_own_selection[clipboard] = 1; - if (clipboard) - fl_update_clipboard(); -} - -// Call this when a "paste" operation happens: -void Fl_WinAPI_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) { - if (!clipboard || (fl_i_own_selection[clipboard] && strcmp(type, Fl::clipboard_plain_text) == 0)) { - // We already have it, do it quickly without window server. - // Notice that the text is clobbered if set_selection is - // called in response to FL_PASTE! - char *i = fl_selection_buffer[clipboard]; - if (i == 0L) { - Fl::e_text = 0; - return; - } - char *clip_text = new char[fl_selection_length[clipboard] + 1]; - char *o = clip_text; - while (*i) { // Convert \r\n -> \n - if (*i == '\r' && *(i + 1) == '\n') - i++; - else - *o++ = *i++; - } - *o = 0; - Fl::e_text = clip_text; - Fl::e_length = (int)(o - Fl::e_text); - Fl::e_clipboard_type = Fl::clipboard_plain_text; - receiver.handle(FL_PASTE); // this may change Fl::e_text - delete[] clip_text; - Fl::e_text = 0; - } else if (clipboard) { - HANDLE h; - if (!OpenClipboard(NULL)) - return; - if (strcmp(type, Fl::clipboard_plain_text) == 0) { // we want plain text from clipboard - if ((h = GetClipboardData(CF_UNICODETEXT))) { // there's text in the clipboard - wchar_t *memLock = (wchar_t *)GlobalLock(h); - size_t utf16_len = wcslen(memLock); - char *clip_text = new char[utf16_len * 4 + 1]; - unsigned utf8_len = fl_utf8fromwc(clip_text, (unsigned)(utf16_len * 4), memLock, (unsigned)utf16_len); - *(clip_text + utf8_len) = 0; - GlobalUnlock(h); - LPSTR a, b; - a = b = clip_text; - while (*a) { // strip the CRLF pairs ($%$#@^) - if (*a == '\r' && a[1] == '\n') - a++; - else - *b++ = *a++; - } - *b = 0; - Fl::e_text = clip_text; - Fl::e_length = (int)(b - Fl::e_text); - Fl::e_clipboard_type = Fl::clipboard_plain_text; // indicates that the paste event is for plain UTF8 text - receiver.handle(FL_PASTE); // send the FL_PASTE event to the widget. May change Fl::e_text - delete[] clip_text; - Fl::e_text = 0; - } - } else if (strcmp(type, Fl::clipboard_image) == 0) { // we want an image from clipboard - uchar *rgb = NULL; - Fl_RGB_Image *image = NULL; - int width = 0, height = 0, depth = 0; - if ((h = GetClipboardData(CF_DIB))) { // if there's a DIB in clipboard - LPBITMAPINFO lpBI = (LPBITMAPINFO)GlobalLock(h); - width = lpBI->bmiHeader.biWidth; // bitmap width & height - height = lpBI->bmiHeader.biHeight; // is < 0 for top-down DIB - if ((lpBI->bmiHeader.biBitCount == 24 || lpBI->bmiHeader.biBitCount == 32) && - lpBI->bmiHeader.biCompression == BI_RGB && - lpBI->bmiHeader.biClrUsed == 0) { // direct use of the DIB data if it's RGB or RGBA - int linewidth; // row length - depth = lpBI->bmiHeader.biBitCount / 8; // 3 or 4 - if (depth == 3) - linewidth = 4 * ((3 * width + 3) / 4); // row length: series of groups of 3 bytes, rounded to multiple of 4 bytes - else - linewidth = 4 * width; - rgb = new uchar[width * abs(height) * depth]; // will hold the image data - uchar *p = rgb, *r, rr, gg, bb; - int step = (height > 0 ? -1 : +1); - int from = (height > 0 ? height-1 : 0); - int to = (height > 0 ? 0 : -height-1); - for (int i = from; (height > 0 ? i>=to : i <=to); i += step) {// for each row, from last to first - r = (uchar *)(lpBI->bmiColors) + i * linewidth; // beginning of pixel data for the ith row - for (int j = 0; j < width; j++) { // for each pixel in a row - bb = *r++; // BGR is in DIB - gg = *r++; - rr = *r++; - *p++ = rr; // we want RGB - *p++ = gg; - *p++ = bb; - if (depth == 4) - *p++ = *r++; // copy alpha if present - } - } - } else { // the system will decode a complex DIB - void *pDIBBits = (void *)(lpBI->bmiColors + 256); - if (lpBI->bmiHeader.biCompression == BI_BITFIELDS) - pDIBBits = (void *)(lpBI->bmiColors + 3); - else if (lpBI->bmiHeader.biClrUsed > 0) - pDIBBits = (void *)(lpBI->bmiColors + lpBI->bmiHeader.biClrUsed); - Fl_Image_Surface *surf = new Fl_Image_Surface(width, abs(height)); - Fl_Surface_Device::push_current(surf); - SetDIBitsToDevice((HDC)fl_graphics_driver->gc(), 0, 0, width, abs(height), 0, 0, 0, abs(height), pDIBBits, lpBI, DIB_RGB_COLORS); - rgb = fl_read_image(NULL, 0, 0, width, abs(height)); - depth = 3; - Fl_Surface_Device::pop_current(); - delete surf; - } - GlobalUnlock(h); - } else if ((h = GetClipboardData(CF_ENHMETAFILE))) { // if there's an enhanced metafile in clipboard - ENHMETAHEADER header; - GetEnhMetaFileHeader((HENHMETAFILE)h, sizeof(header), &header); // get structure containing metafile dimensions - width = (header.rclFrame.right - header.rclFrame.left + 1); // in .01 mm units - height = (header.rclFrame.bottom - header.rclFrame.top + 1); - HDC hdc = GetDC(NULL); // get unit correspondance between .01 mm and screen pixels - int hmm = GetDeviceCaps(hdc, HORZSIZE); - int hdots = GetDeviceCaps(hdc, HORZRES); - ReleaseDC(NULL, hdc); - float factor = (100.f * hmm) / hdots; - float scaling = Fl::screen_driver()->scale(Fl_Window_Driver::driver(receiver.top_window())->screen_num()); - if (!Fl_Window::current()) { - Fl_GDI_Graphics_Driver *d = (Fl_GDI_Graphics_Driver*)&Fl_Graphics_Driver::default_driver(); - d->scale(scaling);// may run early at app startup before Fl_Window::make_current() scales d - } - width = int(width / (scaling * factor)); // convert to screen pixel unit - height = int(height / (scaling * factor)); - RECT rect = {0, 0, width, height}; - Fl_Image_Surface *surf = new Fl_Image_Surface(width, height, 1); - Fl_Surface_Device::push_current(surf); - fl_color(FL_WHITE); // draw white background - fl_rectf(0, 0, width, height); - rect.right = LONG(rect.right * scaling); // apply scaling to the metafile draw operation - rect.bottom = LONG(rect.bottom * scaling); - PlayEnhMetaFile((HDC)fl_graphics_driver->gc(), (HENHMETAFILE)h, &rect); // draw metafile to offscreen buffer - image = surf->image(); - Fl_Surface_Device::pop_current(); - delete surf; - } - if (rgb || image) { - if (!image) { - image = new Fl_RGB_Image(rgb, width, abs(height), depth); // create new image from pixel data - image->alloc_array = 1; - } - Fl::e_clipboard_data = image; - Fl::e_clipboard_type = Fl::clipboard_image; // indicates that the paste event is for image data - int done = receiver.handle(FL_PASTE); // send FL_PASTE event to widget - Fl::e_clipboard_type = ""; - if (done == 0) { // if widget did not handle the event, delete the image - Fl::e_clipboard_data = NULL; - delete image; - } - } - } - CloseClipboard(); - } -} - -int Fl_WinAPI_Screen_Driver::clipboard_contains(const char *type) { - int retval = 0; - if (!OpenClipboard(NULL)) - return 0; - if (strcmp(type, Fl::clipboard_plain_text) == 0 || type[0] == 0) { - retval = IsClipboardFormatAvailable(CF_UNICODETEXT); - } else if (strcmp(type, Fl::clipboard_image) == 0) { - retval = IsClipboardFormatAvailable(CF_DIB) || IsClipboardFormatAvailable(CF_ENHMETAFILE); - } - CloseClipboard(); - return retval; -} - -static void fl_clipboard_notify_target(HWND wnd) { - if (clipboard_wnd) - return; - - // We get one fake WM_DRAWCLIPBOARD immediately, which we therefore - // need to ignore. - initial_clipboard = true; - - clipboard_wnd = wnd; - next_clipboard_wnd = SetClipboardViewer(wnd); -} - -static void fl_clipboard_notify_untarget(HWND wnd) { - if (wnd != clipboard_wnd) - return; - - // We might be called late in the cleanup where Windows has already - // implicitly destroyed our clipboard window. At that point we need - // to do some extra work to manually repair the clipboard chain. - if (IsWindow(wnd)) - ChangeClipboardChain(wnd, next_clipboard_wnd); - else { - HWND tmp, head; - - tmp = CreateWindow("STATIC", "Temporary FLTK Clipboard Window", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); - if (tmp == NULL) - return; - - head = SetClipboardViewer(tmp); - if (head == NULL) - ChangeClipboardChain(tmp, next_clipboard_wnd); - else { - SendMessage(head, WM_CHANGECBCHAIN, (WPARAM)wnd, (LPARAM)next_clipboard_wnd); - ChangeClipboardChain(tmp, head); - } - - DestroyWindow(tmp); - } - - clipboard_wnd = next_clipboard_wnd = 0; -} - -void fl_clipboard_notify_retarget(HWND wnd) { - // The given window is getting destroyed. If it's part of the - // clipboard chain then we need to unregister it and find a - // replacement window. - if (wnd != clipboard_wnd) - return; - - fl_clipboard_notify_untarget(wnd); - - if (Fl::first_window()) - fl_clipboard_notify_target(fl_xid(Fl::first_window())); -} - -void Fl_WinAPI_Screen_Driver::clipboard_notify_change() { - // untarget clipboard monitor if no handlers are registered - if (clipboard_wnd != NULL && fl_clipboard_notify_empty()) { - fl_clipboard_notify_untarget(clipboard_wnd); - return; - } - - // if there are clipboard notify handlers but no window targeted - // target first window if available - if (clipboard_wnd == NULL && Fl::first_window()) - fl_clipboard_notify_target(fl_xid(Fl::first_window())); -} - -//////////////////////////////////////////////////////////////// -void fl_get_codepage() { - HKL hkl = GetKeyboardLayout(0); - TCHAR ld[8]; - - GetLocaleInfo(LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE, ld, 6); - DWORD ccp = atol(ld); - fl_codepage = ccp; -} - -HWND fl_capture; - -// \param[in] window The FLTK window that generated the event -// \param[in] what 0 = down, 1 = double down, 2 = up, 3 = move -// \param[in] button 1 = left, 2 = middle, 3 = right, 4 = XBUTTON1, 5 = any other XBUTTON -// \param[in] wParam depends on event, for example MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2 -// \param[in] lParam 32 bit value, top 16 bit are the signed y coordinate, bottom 16 bits is x in window space -// WM_KILLFOCUS calls this with (Fl::grab(), 0, 1, MK_LBUTTON, MAKELPARAM(32767, 0)) -static int mouse_event(Fl_Window *window, int what, int button, - WPARAM wParam, LPARAM lParam) { - static int px, py, pmx, pmy; - POINT pt; - float scale = Fl::screen_driver()->scale(window->screen_num()); - Fl::e_x = pt.x = (signed short)LOWORD(lParam); - Fl::e_y = pt.y = (signed short)HIWORD(lParam); - Fl::e_x = int(Fl::e_x / scale); - Fl::e_y = int(Fl::e_y / scale); - ClientToScreen(fl_xid(window), &pt); - Fl::e_x_root = int(pt.x / scale); - Fl::e_y_root = int(pt.y / scale); -#ifdef USE_CAPTURE_MOUSE_WIN - Fl_Window *mouse_window = window; // save "mouse window" -#endif - while (window->parent()) { - Fl::e_x += window->x(); - Fl::e_y += window->y(); - window = window->window(); - } - - ulong state = Fl::e_state & 0xff0000; // keep shift key states -#if 0 - // mouse event reports some shift flags, perhaps save them? - if (wParam & MK_SHIFT) state |= FL_SHIFT; - if (wParam & MK_CONTROL) state |= FL_CTRL; -#endif - if (wParam & MK_LBUTTON) state |= FL_BUTTON1; // left - if (wParam & MK_MBUTTON) state |= FL_BUTTON2; // right - if (wParam & MK_RBUTTON) state |= FL_BUTTON3; // middle - if (wParam & MK_XBUTTON1) state |= FL_BUTTON4; // side button 1 (back) - if (wParam & MK_XBUTTON2) state |= FL_BUTTON5; // side button 2 (forward) - - Fl::e_state = state; - - switch (what) { - case 1: // double-click - if (Fl::e_is_click) { - Fl::e_clicks++; - goto J1; - } - case 0: // single-click - Fl::e_clicks = 0; - J1: -#ifdef USE_CAPTURE_MOUSE_WIN - if (!fl_capture) - SetCapture(fl_xid(mouse_window)); // use mouse window -#else - if (!fl_capture) - SetCapture(fl_xid(window)); // use main window -#endif - Fl::e_keysym = FL_Button + button; - Fl::e_is_click = 1; - px = pmx = Fl::e_x_root; - py = pmy = Fl::e_y_root; - return Fl::handle(FL_PUSH, window); - - case 2: // release: - if (!fl_capture) - ReleaseCapture(); - Fl::e_keysym = FL_Button + button; - return Fl::handle(FL_RELEASE, window); - - case 3: // move: - default: // avoid compiler warning - // Windows produces extra events even if the mouse does not move, ignore em: - if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) - return 1; - pmx = Fl::e_x_root; - pmy = Fl::e_y_root; - if (abs(Fl::e_x_root - px) > 5 || abs(Fl::e_y_root - py) > 5) - Fl::e_is_click = 0; - return Fl::handle(FL_MOVE, window); - } -} - -// Convert a Windows VK_x to an FLTK (X) Keysym: -// See also the inverse converter in Fl_get_key_win32.cxx -// This table is in numeric order by VK: -static const struct { - unsigned short vk, fltk, extended; -} vktab[] = { - {VK_BACK, FL_BackSpace}, - {VK_TAB, FL_Tab}, - {VK_CLEAR, FL_KP+'5', 0xff0b/*XK_Clear*/}, - {VK_RETURN, FL_Enter, FL_KP_Enter}, - {VK_SHIFT, FL_Shift_L, FL_Shift_R}, - {VK_CONTROL, FL_Control_L, FL_Control_R}, - {VK_MENU, FL_Alt_L, FL_Alt_R}, - {VK_PAUSE, FL_Pause}, - {VK_CAPITAL, FL_Caps_Lock}, - {VK_ESCAPE, FL_Escape}, - {VK_SPACE, ' '}, - {VK_PRIOR, FL_KP+'9', FL_Page_Up}, - {VK_NEXT, FL_KP+'3', FL_Page_Down}, - {VK_END, FL_KP+'1', FL_End}, - {VK_HOME, FL_KP+'7', FL_Home}, - {VK_LEFT, FL_KP+'4', FL_Left}, - {VK_UP, FL_KP+'8', FL_Up}, - {VK_RIGHT, FL_KP+'6', FL_Right}, - {VK_DOWN, FL_KP+'2', FL_Down}, - {VK_SNAPSHOT, FL_Print}, // does not work on NT - {VK_INSERT, FL_KP+'0', FL_Insert}, - {VK_DELETE, FL_KP+'.', FL_Delete}, - {VK_LWIN, FL_Meta_L}, - {VK_RWIN, FL_Meta_R}, - {VK_APPS, FL_Menu}, - {VK_SLEEP, FL_Sleep}, - {VK_MULTIPLY, FL_KP+'*'}, - {VK_ADD, FL_KP+'+'}, - {VK_SUBTRACT, FL_KP+'-'}, - {VK_DECIMAL, FL_KP+'.'}, - {VK_DIVIDE, FL_KP+'/'}, - {VK_NUMLOCK, FL_Num_Lock}, - {VK_SCROLL, FL_Scroll_Lock}, -#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500) - {VK_BROWSER_BACK, FL_Back}, - {VK_BROWSER_FORWARD, FL_Forward}, - {VK_BROWSER_REFRESH, FL_Refresh}, - {VK_BROWSER_STOP, FL_Stop}, - {VK_BROWSER_SEARCH, FL_Search}, - {VK_BROWSER_FAVORITES, FL_Favorites}, - {VK_BROWSER_HOME, FL_Home_Page}, - {VK_VOLUME_MUTE, FL_Volume_Mute}, - {VK_VOLUME_DOWN, FL_Volume_Down}, - {VK_VOLUME_UP, FL_Volume_Up}, - {VK_MEDIA_NEXT_TRACK, FL_Media_Next}, - {VK_MEDIA_PREV_TRACK, FL_Media_Prev}, - {VK_MEDIA_STOP, FL_Media_Stop}, - {VK_MEDIA_PLAY_PAUSE, FL_Media_Play}, - {VK_LAUNCH_MAIL, FL_Mail}, -#endif - {0xba, ';'}, - {0xbb, '='}, // 0xbb == VK_OEM_PLUS (see #1086) - {0xbc, ','}, - {0xbd, '-'}, - {0xbe, '.'}, - {0xbf, '/'}, - {0xc0, '`'}, - {0xdb, '['}, - {0xdc, '\\'}, - {0xdd, ']'}, - {0xde, '\''}, - {VK_OEM_102, FL_Iso_Key} -}; -static int ms2fltk(WPARAM vk, int extended) { - static unsigned short vklut[256]; - static unsigned short extendedlut[256]; - if (!vklut[1]) { // init the table - unsigned int i; - for (i = 0; i < 256; i++) - vklut[i] = tolower(i); - for (i = VK_F1; i <= VK_F16; i++) - vklut[i] = i + (FL_F - (VK_F1 - 1)); - for (i = VK_NUMPAD0; i <= VK_NUMPAD9; i++) - vklut[i] = i + (FL_KP + '0' - VK_NUMPAD0); - for (i = 0; i < sizeof(vktab) / sizeof(*vktab); i++) { - vklut[vktab[i].vk] = vktab[i].fltk; - extendedlut[vktab[i].vk] = vktab[i].extended; - } - for (i = 0; i < 256; i++) - if (!extendedlut[i]) - extendedlut[i] = vklut[i]; - } - return extended ? extendedlut[vk] : vklut[vk]; -} - -#if USE_COLORMAP -extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx -#endif - - -static Fl_Window *resize_bug_fix; -static bool moving_window = false; // true when dragging a window with the mouse on the titlebar - -extern void fl_save_pen(void); -extern void fl_restore_pen(void); -extern LRESULT fl_win32_tablet_handler(MSG& msg); - - -static void invalidate_gl_win(Fl_Window *glwin) { - static Fl_WinAPI_Plugin *plugin = NULL; - if (!plugin) { - Fl_Plugin_Manager pm("winapi.fltk.org"); - plugin = (Fl_WinAPI_Plugin*)pm.plugin("gl.winapi.fltk.org"); - } - plugin->invalidate(glwin); -} - - -static BOOL CALLBACK child_window_cb(HWND child_xid, LPARAM data) { - Fl_Window *child = fl_find(child_xid); - if (data) { - float s = *(float*)data; - SetWindowPos(child_xid, 0, int(round(child->x() * s)), int(round(child->y() * s)), - int(round(child->w() * s)), int(round(child->h() * s)), 0); - } - if (child->as_gl_window()) invalidate_gl_win(child); - return TRUE; -} - - -static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - - // Copy the message to fl_msg so add_handler code can see it. - // It is already there if this is called by DispatchMessage, - // but not if Windows calls this directly. - - fl_msg.hwnd = hWnd; - fl_msg.message = uMsg; - fl_msg.wParam = wParam; - fl_msg.lParam = lParam; - // fl_msg.time = ??? - // fl_msg.pt = ??? - // fl_msg.lPrivate = ??? - - Fl_Window *window = fl_find(hWnd); - float scale = (window ? Fl::screen_driver()->scale(Fl_Window_Driver::driver(window)->screen_num()) : 1); - - if (window) { - switch (uMsg) { - - case WM_DPICHANGED: { // 0x02E0, after display re-scaling and followed by WM_DISPLAYCHANGE - if (is_dpi_aware) { - RECT r, *lParam_rect = (RECT*)lParam; - Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver(); - int centerX = (lParam_rect->left + lParam_rect->right)/2; - int centerY = (lParam_rect->top + lParam_rect->bottom)/2; - int ns = sd->screen_num_unscaled(centerX, centerY); - int old_ns = Fl_Window_Driver::driver(window)->screen_num(); - if (sd->dpi[ns][0] != HIWORD(wParam) && ns == old_ns) { // change DPI of a screen - sd->dpi[ns][0] = sd->dpi[ns][1] = HIWORD(wParam); - float f = HIWORD(wParam) / 96.f; - GetClientRect(hWnd, &r); - float old_f = float(r.right) / window->w(); - Fl::screen_driver()->scale(ns, f); - Fl_Window_Driver::driver(window)->resize_after_scale_change(ns, old_f, f); - sd->update_scaling_capability(); - } else if (ns != old_ns) { - // jump window with Windows+Shift+L|R-arrow to other screen with other DPI - if (ns >= 0) Fl_Window_Driver::driver(window)->screen_num(ns); - UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOCOPYBITS; - SetWindowPos(hWnd, NULL, lParam_rect->left, lParam_rect->top, - lParam_rect->right - lParam_rect->left, - lParam_rect->bottom - lParam_rect->top, flags); - if (ns >= 0) { - scale = Fl::screen_driver()->scale(ns); - EnumChildWindows(hWnd, child_window_cb, (LPARAM)&scale); - } - } - } - return 0; - } - - case WM_QUIT: // this should not happen? - Fl::fatal("WM_QUIT message"); - - case WM_CLOSE: // user clicked close box - Fl::handle(FL_CLOSE, window); - return 0; - - case WM_SYNCPAINT: - case WM_NCPAINT: - case WM_ERASEBKGND: - // Andreas Weitl - WM_SYNCPAINT needs to be passed to DefWindowProc - // so that Windows can generate the proper paint messages... - // Similarly, WM_NCPAINT and WM_ERASEBKGND need this, too... - break; - - case WM_PAINT: { - HRGN R, R2; - Fl_X *i = Fl_X::flx(window); - Fl_Window_Driver::driver(window)->wait_for_expose_value = 0; - char redraw_whole_window = false; - if (!i->region && window->damage()) { - // Redraw the whole window... - i->region = CreateRectRgn(0, 0, window->w(), window->h()); - redraw_whole_window = true; - } - - // We need to merge Windows' damage into FLTK's damage. - R = CreateRectRgn(0, 0, 0, 0); - int r = GetUpdateRgn(hWnd, R, 0); - if (r == NULLREGION && !redraw_whole_window) { - DeleteObject(R); - break; - } - - // convert i->region in FLTK units to R2 in drawing units - R2 = Fl_GDI_Graphics_Driver::scale_region((HRGN)i->region, scale, NULL); - - RECT r_box; - if (scale != 1 && GetRgnBox(R, &r_box) != NULLREGION) { - // add de-scaled update region to i->region in FLTK units - r_box.left = LONG(r_box.left / scale); - r_box.right = LONG(r_box.right / scale); - r_box.top = LONG(r_box.top / scale); - r_box.bottom = LONG(r_box.bottom / scale); - HRGN R3 = CreateRectRgn(r_box.left, r_box.top, r_box.right + 1, r_box.bottom + 1); - if (!i->region) i->region = R3; - else { - CombineRgn((HRGN)i->region, (HRGN)i->region, R3, RGN_OR); - DeleteObject(R3); - } - } - if (R2) { - // Also tell Windows that we are drawing someplace else as well... - CombineRgn(R2, R2, R, RGN_OR); - DeleteObject(R); - } else { - R2 = R; - } - if (window->type() == FL_DOUBLE_WINDOW) - ValidateRgn(hWnd, 0); - else { - ValidateRgn(hWnd, R2); - } - - if (scale != 1) DeleteObject(R2); - - window->clear_damage((uchar)(window->damage() | FL_DAMAGE_EXPOSE)); - // These next two statements should not be here, so that all update - // is deferred until Fl::flush() is called during idle. However Windows - // apparently is very unhappy if we don't obey it and draw right now. - // Very annoying! - fl_GetDC(hWnd); // Make sure we have a DC for this window... - fl_save_pen(); - Fl_Window_Driver::driver(window)->flush(); - fl_restore_pen(); - window->clear_damage(); - return 0; - } // case WM_PAINT - - case WM_LBUTTONDOWN: - mouse_event(window, 0, 1, wParam, lParam); - return 0; - case WM_LBUTTONDBLCLK: - mouse_event(window, 1, 1, wParam, lParam); - return 0; - case WM_LBUTTONUP: - mouse_event(window, 2, 1, wParam, lParam); - return 0; - case WM_MBUTTONDOWN: - mouse_event(window, 0, 2, wParam, lParam); - return 0; - case WM_MBUTTONDBLCLK: - mouse_event(window, 1, 2, wParam, lParam); - return 0; - case WM_MBUTTONUP: - mouse_event(window, 2, 2, wParam, lParam); - return 0; - case WM_RBUTTONDOWN: - mouse_event(window, 0, 3, wParam, lParam); - return 0; - case WM_RBUTTONDBLCLK: - mouse_event(window, 1, 3, wParam, lParam); - return 0; - case WM_RBUTTONUP: - mouse_event(window, 2, 3, wParam, lParam); - return 0; - case WM_XBUTTONDOWN: { - int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5; - mouse_event(window, 0, xbutton, wParam, lParam); - return 0; - } - case WM_XBUTTONDBLCLK: { - int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5; - mouse_event(window, 1, xbutton, wParam, lParam); - return 0; - } - case WM_XBUTTONUP: { - int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5; - mouse_event(window, 2, xbutton, wParam, lParam); - return 0; - } - - case WM_MOUSEMOVE: -#ifdef USE_TRACK_MOUSE - if (track_mouse_win != window) { - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - _TrackMouseEvent(&tme); - track_mouse_win = window; - } -#endif // USE_TRACK_MOUSE - mouse_event(window, 3, 0, wParam, lParam); - return 0; - - case WM_MOUSELEAVE: - if (track_mouse_win == window) { // we left the top level window ! - Fl_Window *tw = window; - while (tw->parent()) // find top level window - tw = tw->window(); - // Get a better mouse position for FL_LEAVE event (#87) - POINT pt; - if (GetCursorPos(&pt)) { - float scale = Fl::screen_driver()->scale(tw->screen_num()); - Fl::e_x_root = int(pt.x / scale); - Fl::e_y_root = int(pt.y / scale); - ScreenToClient(fl_xid(tw), &pt); - Fl::e_x = clamp(int(pt.x / scale), 0, window->w() - 1); - Fl::e_y = clamp(int(pt.y / scale), 0, window->h() - 1); - } - Fl::belowmouse(0); - Fl::handle(FL_LEAVE, tw); - } - track_mouse_win = 0; // force TrackMouseEvent() restart - break; - - case WM_SETFOCUS: - if ((Fl::modal_) && (Fl::modal_ != window)) { - SetFocus(fl_xid(Fl::modal_)); - return 0; - } - Fl::handle(FL_FOCUS, window); - break; - - case WM_KILLFOCUS: - if (Fl::grab() && (Fl::grab() != window) && Fl::grab()->menu_window()) { - // simulate click at remote location (see issue #1166), coordinates are signed 16 bit values - mouse_event(Fl::grab(), 0, 1, MK_LBUTTON, MAKELPARAM(32767, 0)); - } - Fl::handle(FL_UNFOCUS, window); - Fl::flush(); // it never returns to main loop when deactivated... - break; - - case WM_SHOWWINDOW: - if (!window->parent()) { - Fl::handle(wParam ? FL_SHOW : FL_HIDE, window); - } - break; - - case WM_ACTIVATEAPP: - // From eric@vfx.sel.sony.com, we should process WM_ACTIVATEAPP - // messages to restore the correct state of the shift/ctrl/alt/lock - // keys... Added control, shift, alt, and meta keys, and changed - // to use GetAsyncKeyState and do it when wParam is 1 - // (that means we have focus...) - if (wParam) { - ulong state = 0; - if (GetAsyncKeyState(VK_CAPITAL)) - state |= FL_CAPS_LOCK; - if (GetAsyncKeyState(VK_NUMLOCK)) - state |= FL_NUM_LOCK; - if (GetAsyncKeyState(VK_SCROLL)) - state |= FL_SCROLL_LOCK; - if (GetAsyncKeyState(VK_CONTROL) & ~1) - state |= FL_CTRL; - if (GetAsyncKeyState(VK_SHIFT) & ~1) - state |= FL_SHIFT; - if (GetAsyncKeyState(VK_MENU)) - state |= FL_ALT; - if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & ~1) - state |= FL_META; - Fl::e_state = state; - Fl::handle(FL_APP_ACTIVATE, nullptr); - } else { - Fl::handle(FL_APP_DEACTIVATE, nullptr); - } - break; - - case WM_INPUTLANGCHANGE: - fl_get_codepage(); - break; - case WM_IME_COMPOSITION: - // if (!fl_is_nt4() && lParam & GCS_RESULTCLAUSE) { - // HIMC himc = ImmGetContext(hWnd); - // wlen = ImmGetCompositionStringW(himc, GCS_RESULTSTR, - // wbuf, sizeof(wbuf)) / sizeof(short); - // if (wlen < 0) wlen = 0; - // wbuf[wlen] = 0; - // ImmReleaseContext(hWnd, himc); - // } - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - case WM_KEYUP: - case WM_SYSKEYUP: - // save the keysym until we figure out the characters: - Fl::e_keysym = Fl::e_original_keysym = ms2fltk(wParam, lParam & (1 << 24)); - // Kludge to allow recognizing ctrl+'-' on keyboards with digits in uppercase positions (e.g. French) - if (Fl::e_keysym == '6' && (VkKeyScanA('-') & 0xff) == '6') { - Fl::e_keysym = '-'; - } - // See if TranslateMessage turned it into a WM_*CHAR message: - if (PeekMessageW(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) { - uMsg = fl_msg.message; - wParam = fl_msg.wParam; - lParam = fl_msg.lParam; - } - // FALLTHROUGH ... - - case WM_DEADCHAR: - case WM_SYSDEADCHAR: - case WM_CHAR: - case WM_SYSCHAR: { - ulong state = Fl::e_state & 0xff000000; // keep the mouse button state - // if GetKeyState is expensive we might want to comment some of these out: - if (GetKeyState(VK_SHIFT) & ~1) - state |= FL_SHIFT; - if (GetKeyState(VK_CAPITAL)) - state |= FL_CAPS_LOCK; - if (GetKeyState(VK_CONTROL) & ~1) - state |= FL_CTRL; - // Alt gets reported for the Alt-GR switch on non-English keyboards. - // so we need to check the event as well to get it right: - if ((lParam & (1 << 29)) // same as GetKeyState(VK_MENU) - && uMsg != WM_CHAR) - state |= FL_ALT; - if (GetKeyState(VK_NUMLOCK)) - state |= FL_NUM_LOCK; - if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & ~1) { - // Windows bug? GetKeyState returns garbage if the user hit the - // meta key to pop up start menu. Sigh. - if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & ~1) - state |= FL_META; - } - if (GetKeyState(VK_SCROLL)) - state |= FL_SCROLL_LOCK; - Fl::e_state = state; - static char buffer[1024]; - if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) { - wchar_t u = (wchar_t)wParam; - // Windows emoji palette triggered with Windows + dot sends 2 or more WM_CHAR messages: - // the 2 components of a surrogate pair, or variation selectors, or zero-width joiner, - // or emoji modifiers FITZPATRICK or extra Unicode points. - if (u >= 0xD800 && u <= 0xDFFF) { // handle the 2 components of a surrogate pair - static wchar_t surrogate_pair[2]; - if (IS_HIGH_SURROGATE(u)) { - surrogate_pair[0] = u; // memorize the 1st member of the pair - Fl::e_length = 0; - return 0; // and wait for next WM_CHAR message that will give the 2nd member - } else { - surrogate_pair[1] = u; // memorize the 2nd member of the pair - Fl::e_length = fl_utf8fromwc(buffer, 1024, surrogate_pair, 2); // transform to UTF-8 - } - } else { - Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); // process regular Unicode point - } - buffer[Fl::e_length] = 0; - } else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) { - if (state & FL_NUM_LOCK) { - // Convert to regular keypress... - buffer[0] = Fl::e_keysym - FL_KP; - Fl::e_length = 1; - } else { - // Convert to special keypress... - buffer[0] = 0; - Fl::e_length = 0; - switch (Fl::e_keysym) { - case FL_KP + '0': - Fl::e_keysym = FL_Insert; - break; - case FL_KP + '1': - Fl::e_keysym = FL_End; - break; - case FL_KP + '2': - Fl::e_keysym = FL_Down; - break; - case FL_KP + '3': - Fl::e_keysym = FL_Page_Down; - break; - case FL_KP + '4': - Fl::e_keysym = FL_Left; - break; - case FL_KP + '6': - Fl::e_keysym = FL_Right; - break; - case FL_KP + '7': - Fl::e_keysym = FL_Home; - break; - case FL_KP + '8': - Fl::e_keysym = FL_Up; - break; - case FL_KP + '9': - Fl::e_keysym = FL_Page_Up; - break; - case FL_KP + '.': - Fl::e_keysym = FL_Delete; - break; - case FL_KP + '/': - case FL_KP + '*': - case FL_KP + '-': - case FL_KP + '+': - buffer[0] = Fl::e_keysym - FL_KP; - Fl::e_length = 1; - break; - } - } - } else if ((lParam & (1 << 31)) == 0) { -#ifdef FLTK_PREVIEW_DEAD_KEYS - if ((lParam & (1 << 24)) == 0) { // clear if dead key (always?) - wchar_t u = (wchar_t)wParam; - Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); - buffer[Fl::e_length] = 0; - } else { // set if "extended key" (never printable?) - buffer[0] = 0; - Fl::e_length = 0; - } -#else - buffer[0] = 0; - Fl::e_length = 0; -#endif - } - Fl::e_text = buffer; - - // Kludge to process the +-containing key in cross-platform way when used with Ctrl -/* Table of how Windows processes the '+'-containing key by keyboard layout - key virtual -content key keyboard layout - =|+ 0xbb US/UK/Fr/Arabic/Chinese/Hebrew/Brazil/Russian/Vietnam/Japan/Korean/Persian - +|* 0xbb German/Spanish/Italy/Greek/Portugal - +|? 0xbb Swedish/Finish/Norway - +|± 0xbb Dutch - 1|+ '1' Swiss/Luxemburg - 3|+ '3' Hungarian - 4|+ '4' Turkish -*/ - if ((Fl::e_state & FL_CTRL) && !(GetAsyncKeyState(VK_MENU) >> 15)) { - // extra processing necessary only when Ctrl is down and Alt is up - int vk_plus_key = (VkKeyScanA('+') & 0xff); // virtual key of '+'-containing key - bool plus_shift_pos = ((VkKeyScanA('+') & 0x100) != 0); // true means '+' in shifted position - int plus_other_char; // the other char on same key as '+' - if (plus_shift_pos) plus_other_char = ms2fltk(vk_plus_key, 0); - else if ((VkKeyScanA('*') & 0xff) == vk_plus_key) plus_other_char = '*'; // German - else if ((VkKeyScanA('?') & 0xff) == vk_plus_key) plus_other_char = '?'; // Swedish - else if ((VkKeyScanW(L'±') & 0xff) == vk_plus_key) plus_other_char = L'±'; // Dutch - else plus_other_char = '='; // fallback -//fprintf(stderr, "plus_shift_pos=%d plus_other_char='%c' vk+=0x%x\n", plus_shift_pos, -// plus_other_char, vk_plus_key); - if ( (vk_plus_key == 0xbb && Fl::e_keysym == '=') || // the '+'-containing key is down - (plus_shift_pos && Fl::e_keysym == plus_other_char) ) { - Fl::e_keysym = (plus_shift_pos ? plus_other_char : '+'); - static char plus_other_char_utf8[4]; - int lutf8 = fl_utf8encode(plus_other_char, plus_other_char_utf8); - plus_other_char_utf8[lutf8] = 0; - if (plus_shift_pos) { - Fl::e_text = ( (Fl::e_state & FL_SHIFT) ? (char*)"+" : plus_other_char_utf8 ); - } else { - Fl::e_text = ( (Fl::e_state & FL_SHIFT) ? plus_other_char_utf8 : (char*)"+" ); - } - Fl::e_length = (int)strlen(Fl::e_text); - } - } - // end of processing of the +-containing key - - if (lParam & (1 << 31)) { // key up events. - if (Fl::handle(FL_KEYUP, window)) - return 0; - break; - } - while (window->parent()) - window = window->window(); - if (Fl::handle(FL_KEYBOARD, window)) { - if (uMsg == WM_DEADCHAR || uMsg == WM_SYSDEADCHAR) - Fl::compose_state = 1; - return 0; - } - break; // WM_KEYDOWN ... WM_SYSKEYUP, WM_DEADCHAR ... WM_SYSCHAR - } // case WM_DEADCHAR ... WM_SYSCHAR - - case WM_MOUSEWHEEL: { - static int delta = 0; // running total of all vertical mousewheel motion - delta += (SHORT)(HIWORD(wParam)); - int dy = -delta / WHEEL_DELTA; - delta += dy * WHEEL_DELTA; - if (dy == 0) // nothing to do - return 0; - if (Fl::event_shift()) { // shift key pressed: send horizontal mousewheel event - Fl::e_dx = dy; - Fl::e_dy = 0; - } else { // shift key not pressed (normal behavior): send vertical mousewheel event - Fl::e_dx = 0; - Fl::e_dy = dy; - } - Fl::handle(FL_MOUSEWHEEL, window); - return 0; - } - - case WM_MOUSEHWHEEL: { - static int delta = 0; // running total of all horizontal mousewheel motion - delta += (SHORT)(HIWORD(wParam)); - int dx = delta / WHEEL_DELTA; - delta -= dx * WHEEL_DELTA; - if (dx == 0) // nothing to do - return 0; - if (Fl::event_shift()) { // shift key pressed: send *vertical* mousewheel event - Fl::e_dx = 0; - Fl::e_dy = dx; - } else { // shift key not pressed (normal behavior): send horizontal mousewheel event - Fl::e_dx = dx; - Fl::e_dy = 0; - } - Fl::handle(FL_MOUSEWHEEL, window); - return 0; - } - - case WM_GETMINMAXINFO: - Fl_WinAPI_Window_Driver::driver(window)->set_minmax((LPMINMAXINFO)lParam); - break; - - case WM_SIZE: - if (!window->parent()) { - Fl_Window_Driver::driver(window)->is_maximized(wParam == SIZE_MAXIMIZED); - if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) { - Fl::handle(FL_HIDE, window); - } else { - if (!moving_window) { - Fl::handle(FL_SHOW, window); - resize_bug_fix = window; - window->size(int(ceil(LOWORD(lParam) / scale)), int(ceil(HIWORD(lParam) / scale))); - } else { - window->size(int(ceil(LOWORD(lParam) / scale)), int(ceil(HIWORD(lParam) / scale))); - EnumChildWindows(hWnd, child_window_cb, (LPARAM)&scale); - window->redraw(); - } - } - } - return 0; - - case WM_MOVING: - moving_window = true; - return 1; - - case WM_CAPTURECHANGED: - moving_window = false; - resize_bug_fix = 0; - return 0; - - case WM_MOVE: { - if (IsIconic(hWnd) || window->parent()) { - break; - } - if (moving_window) resize_bug_fix = window; - POINTS pts = MAKEPOINTS(lParam); - int nx = pts.x, ny = pts.y; - // detect when window centre changes screen - Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver *)Fl::screen_driver(); - Fl_WinAPI_Window_Driver *wd = Fl_WinAPI_Window_Driver::driver(window); - int olds = wd->screen_num(); - // Issue #1097: when a fullscreen window is restored to its size, it receives first a WM_MOVE - // and then a WM_SIZE, so it still has its fullscreen size at the WM_MOVE event, which defeats - // using window->w()|h() to compute the center of the (small) window. We detect this situation - // with condition: !window->fullscreen_active() && *wd->no_fullscreen_w() - // and use *wd->no_fullscreen_w()|h() instead of window->w()|h(). - int trueW = window->w(), trueH = window->h(); - if (!window->fullscreen_active() && *wd->no_fullscreen_w()) { - trueW = *wd->no_fullscreen_w(); trueH = *wd->no_fullscreen_h(); - } - int news = sd->screen_num_unscaled(nx + int(trueW * scale / 2), ny + int(trueH * scale / 2)); - if (news == -1) - news = olds; - scale = sd->scale(news); - wd->x(int(round(nx/scale))); - wd->y(int(round(ny/scale))); - } - return 0; - - case WM_SETCURSOR: - if (LOWORD(lParam) == HTCLIENT) { - while (window->parent()) - window = window->window(); - SetCursor(Fl_WinAPI_Window_Driver::driver(window)->cursor); - return 0; - } - break; - -#if USE_COLORMAP - case WM_QUERYNEWPALETTE: - fl_GetDC(hWnd); - if (fl_select_palette()) - InvalidateRect(hWnd, NULL, FALSE); - break; - - case WM_PALETTECHANGED: - if ((HWND)wParam != hWnd && fl_select_palette()) - UpdateColors(fl_GetDC(hWnd)); - break; - - case WM_CREATE: - fl_GetDC(hWnd); - fl_select_palette(); - break; -#endif - - case WM_DESTROYCLIPBOARD: - fl_i_own_selection[1] = 0; - return 1; - - case WM_DISPLAYCHANGE: {// when screen configuration (number, size, position) changes - Fl::call_screen_init(); - Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); - return 0; - } - case WM_CHANGECBCHAIN: - if ((hWnd == clipboard_wnd) && (next_clipboard_wnd == (HWND)wParam)) - next_clipboard_wnd = (HWND)lParam; - else - SendMessage(next_clipboard_wnd, WM_CHANGECBCHAIN, wParam, lParam); - return 0; - - case WM_DRAWCLIPBOARD: - // When the clipboard moves between two FLTK windows, - // fl_i_own_selection will temporarily be false as we are - // processing this message. Hence the need to use fl_find(). - if (!initial_clipboard && !fl_find(GetClipboardOwner())) - fl_trigger_clipboard_notify(1); - initial_clipboard = false; - - if (next_clipboard_wnd) - SendMessage(next_clipboard_wnd, WM_DRAWCLIPBOARD, wParam, lParam); - - return 0; - - default: { -#if defined(FLTK_HAVE_PEN_SUPPORT) - LRESULT ret = fl_win32_tablet_handler(fl_msg); - if (ret != -1) - return ret; -#endif - if (Fl::handle(0, 0)) - return 0; - break; } - } // switch (uMsg) - } // if (window) - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -/* Implementation note about the API to get the dimensions of the top/left borders and the title bar - - Function fake_X_wm() below is used before calling CreateWindowExW() to create - a window and before calling SetWindowPos(). Both of these Windows functions need the window size - including borders and title bar. Function fake_X_wm() uses AdjustWindowRectExForDpi() or - AdjustWindowRectEx() to get the sizes of borders and title bar. The gotten values don't always match - what is seen on the display, but they are the **required** values so the subsequent calls to - CreateWindowExW() or SetWindowPos() correctly size the window. - The Windows doc of AdjustWindowRectExForDpi/AdjustWindowRectEx makes this very clear: - Calculates the required size of the window rectangle, based on the desired size of the client - rectangle [and the provided DPI]. This window rectangle can then be passed to the CreateWindowEx - function to create a window with a client area of the desired size. - - Conversely, Fl_WinAPI_Window_Driver::border_width_title_bar_height() is used to get - the true sizes of borders and title bar of a mapped window. The correct API for that is - DwmGetWindowAttribute(). - */ - -// ///////////////////////////////////////////////////////////////// -// This function gets the dimensions of the top/left borders and -// the title bar, if there is one, based on the FL_BORDER, FL_MODAL -// and FL_NONMODAL flags, and on the window's size range. -// It returns the following values: -// -// value | border | title bar -// 0 | none | no -// 1 | fix | yes -// 2 | size | yes - -int Fl_WinAPI_Window_Driver::fake_X_wm(int &X, int &Y, int &bt, int &bx, int &by, DWORD style, DWORD styleEx) { - - const Fl_Window *w = pWindow; - - int W = 0, H = 0, xoff = 0, yoff = 0, dx = 0, dy = 0; - int ret = bx = by = bt = 0; - - int fallback = 1; - float s = Fl::screen_driver()->scale(screen_num()); - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (!w->parent()) { - if (fl_xid(w) || style) { - // The block below calculates the window borders by requesting the - // required decorated window rectangle for a desired client rectangle. - // If any part of the function above fails, we will drop to a - // fallback to get the best guess which is always available. - - if (!style) { - HWND hwnd = fl_xid(w); - // request the style flags of this window, as Windows sees them - style = GetWindowLong(hwnd, GWL_STYLE); - styleEx = GetWindowLong(hwnd, GWL_EXSTYLE); - } - - RECT r; - int drawingX, drawingY; // drawing coordinates of window top-left - r.left = drawingX = int(round(w->x() * s)); - r.top = drawingY = int(round(w->y() * s)); - r.right = drawingX + int(w->w() * s); - r.bottom = drawingY + int(w->h() * s); - // get the decoration rectangle for the desired client rectangle - - typedef BOOL(WINAPI* AdjustWindowRectExForDpi_type)(LPRECT, DWORD, BOOL, DWORD, UINT); - static AdjustWindowRectExForDpi_type fl_AdjustWindowRectExForDpi = - (AdjustWindowRectExForDpi_type)GetProcAddress(LoadLibrary("User32.DLL"), "AdjustWindowRectExForDpi"); - BOOL ok; - if (is_dpi_aware && fl_AdjustWindowRectExForDpi) { - Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver(); - UINT dpi = UINT(sd->dpi[screen_num()][0]); - ok = fl_AdjustWindowRectExForDpi(&r, style, FALSE, styleEx, dpi); - } else - ok = AdjustWindowRectEx(&r, style, FALSE, styleEx); - if (ok) { - X = r.left; - Y = r.top; - W = r.right - r.left; - H = r.bottom - r.top; - bx = drawingX - r.left; - by = r.bottom - int(drawingY + w->h() * s); // height of the bottom frame - bt = drawingY - r.top - by; // height of top caption bar - xoff = bx; - yoff = by + bt; - dx = W - int(w->w() * s); - dy = H - int(w->h() * s); - if (maxw != minw || maxh != minh) - ret = 2; - else - ret = 1; - fallback = 0; - } - } - } - // This is the original (pre 1.1.7) routine to calculate window border sizes. - if (fallback) { - if (w->border() && !w->parent()) { - if (maxw != minw || maxh != minh) { - ret = 2; - bx = GetSystemMetrics(SM_CXSIZEFRAME); - by = GetSystemMetrics(SM_CYSIZEFRAME); - } else { - ret = 1; - int padding = GetSystemMetrics(SM_CXPADDEDBORDER); - NONCLIENTMETRICS ncm; - ncm.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); - bx = GetSystemMetrics(SM_CXFIXEDFRAME) + (padding ? padding + ncm.iBorderWidth : 0); - by = GetSystemMetrics(SM_CYFIXEDFRAME) + (padding ? padding + ncm.iBorderWidth : 0); - } - bt = GetSystemMetrics(SM_CYCAPTION); - } - // The coordinates of the whole window, including non-client area - xoff = bx; - yoff = by + bt; - dx = 2 * bx; - dy = 2 * by + bt; - X = w->x() - xoff; - Y = w->y() - yoff; - W = w->w() + dx; - H = w->h() + dy; - } - - // Proceed to positioning the window fully inside the screen, if possible - // Find screen that contains most of the window - // FIXME: this ought to be the "work area" instead of the entire screen ! - int scr_x = 0, scr_y = 0, scr_w = 0, scr_h = 0; - int ns = Fl::screen_num(int(round(X / s)), int(round(Y / s)), int(W / s), int(H / s)); - ((Fl_WinAPI_Screen_Driver*)Fl::screen_driver())->screen_xywh_unscaled(scr_x, scr_y, scr_w, scr_h, ns); - // Make border's lower right corner visible - if (scr_x + scr_w < X + W) - X = scr_x + scr_w - W; - if (scr_y + scr_h < Y + H) - Y = scr_y + scr_h - H; - // Make border's upper left corner visible - if (X < scr_x) - X = scr_x; - if (Y < scr_y) - Y = scr_y; - // Make client area's lower right corner visible - if (scr_x + scr_w < X + dx + w->w()) - X = scr_x + scr_w - int(w->w() * s) - dx; - if (scr_y + scr_h < Y + dy + w->h()) - Y = scr_y + scr_h - int(w->h() * s) - dy; - // Make client area's upper left corner visible - if (X + xoff < scr_x) - X = scr_x - xoff; - if (Y + yoff < scr_y) - Y = scr_y - yoff; - // Return the client area's top left corner in (X,Y) - X += xoff; - Y += yoff; - - if (w->fullscreen_active()) { - bx = by = bt = 0; - } - - return ret; -} - -//////////////////////////////////////////////////////////////// - -static void delayed_fullscreen(Fl_Window *win) { - Fl::remove_check((Fl_Timeout_Handler)delayed_fullscreen, win); - win->fullscreen_off(); - win->fullscreen(); -} - - -static void delayed_maximize(Fl_Window *win) { - Fl::remove_check((Fl_Timeout_Handler)delayed_maximize, win); - win->un_maximize(); - win->maximize(); -} - - -void Fl_WinAPI_Window_Driver::resize(int X, int Y, int W, int H) { -//fprintf(stderr, "resize w()=%d W=%d h()=%d H=%d\n",pWindow->w(), W,pWindow->h(), H); - if (Fl_Window::is_a_rescale() && pWindow->fullscreen_active()) { - Fl::add_check((Fl_Timeout_Handler)delayed_fullscreen, pWindow); - } else if (Fl_Window::is_a_rescale() && pWindow->maximize_active()) { - Fl::add_check((Fl_Timeout_Handler)delayed_maximize, pWindow); - } - UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER; - int is_a_resize = (W != w() || H != h() || Fl_Window::is_a_rescale()); - int resize_from_program = (pWindow != resize_bug_fix); - if (!resize_from_program) - resize_bug_fix = 0; - if (X != x() || Y != y() || Fl_Window::is_a_rescale()) { - force_position(1); - } else { - if (!is_a_resize) - return; - flags |= SWP_NOMOVE; - } - if (is_a_resize) { - if (resize_from_program && shown()) { - // don't obey "resize from program" when window is maximized - WINDOWPLACEMENT wplace; - wplace.length = sizeof(WINDOWPLACEMENT); - BOOL ok = GetWindowPlacement(fl_xid(pWindow), &wplace); - if (ok && wplace.showCmd == SW_SHOWMAXIMIZED) return; - } - pWindow->Fl_Group::resize(X, Y, W, H); - if (visible_r()) { - pWindow->redraw(); - // only wait for exposure if this window has a size - a window - // with no width or height will never get an exposure event - Fl_X *i = Fl_X::flx(pWindow); - if (i && W > 0 && H > 0) - wait_for_expose_value = 1; - } - } else { - x(X); - y(Y); - flags |= SWP_NOSIZE; - } - if (!border()) - flags |= SWP_NOACTIVATE; - if (resize_from_program && shown()) { - int dummy_x, dummy_y, bt, bx, by; - // compute window position and size in scaled units - float s = Fl::screen_driver()->scale(screen_num()); - int scaledX = int(round(X * s)), scaledY = int(round(Y * s)), scaledW = int(W * s), scaledH = int(H * s); - // Ignore window managing when resizing, so that windows (and more - // specifically menus) can be moved offscreen. - if (fake_X_wm(dummy_x, dummy_y, bt, bx, by)) { - scaledX -= bx; - scaledY -= by + bt; - scaledW += 2 * bx; - scaledH += 2 * by + bt; - } - // avoid zero size windows. A zero sized window on Win32 - // will cause continouly new redraw events. - if (scaledW <= 0) - scaledW = 1; - if (scaledH <= 0) - scaledH = 1; - SetWindowPos(fl_xid(pWindow), 0, scaledX, scaledY, scaledW, scaledH, flags); - } -} - - -//////////////////////////////////////////////////////////////// - -/* - This silly little class remembers the name of all window classes - we register to avoid double registration. It has the added bonus - of freeing everything on application close as well. - */ -class NameList { -public: - NameList() { - name = (char **)malloc(sizeof(char **)); - NName = 1; - nName = 0; - } - ~NameList() { - int i; - for (i = 0; i < nName; i++) - free(name[i]); - if (name) - free(name); - } - void add_name(const char *n) { - if (NName == nName) { - NName += 5; - name = (char **)realloc(name, NName * sizeof(char *)); - } - name[nName++] = fl_strdup(n); - } - char has_name(const char *n) { - int i; - for (i = 0; i < nName; i++) { - if (strcmp(name[i], n) == 0) - return 1; - } - return 0; - } - -private: - char **name; - int nName, NName; -}; - -void fl_fix_focus(); // in Fl.cxx - -UINT fl_wake_msg = 0; -int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR - -void Fl_WinAPI_Window_Driver::makeWindow() { - Fl_Group::current(0); // get rid of very common user bug: forgot end() - - fl_open_display(); - - // if the window is a subwindow and our parent is not mapped yet, we - // mark this window visible, so that mapping the parent at a later - // point in time will call this function again to finally map the subwindow. - Fl_Window *w = pWindow; - if (w->parent() && !Fl_X::flx(w->window())) { - w->set_visible(); - return; - } - - static NameList class_name_list; - static const char *first_class_name = 0L; - const char *class_name = w->xclass(); - if (!class_name) - class_name = first_class_name; // reuse first class name used - if (!class_name) - class_name = "FLTK"; // default to create a "FLTK" WNDCLASS - if (!first_class_name) { - first_class_name = class_name; - } -// Prefix user-set window class name by "FLTK", unless it's already here, -// to avoid collision with system-defined window class names (example "edit") - if (strncmp(class_name, "FLTK", 4)) { - static char new_class_name[100]; - snprintf(new_class_name, sizeof(new_class_name), "FLTK-%s", class_name); - class_name = new_class_name; - } - //fprintf(stderr,"makeWindow: class_name=%s\n",class_name);fflush(stderr); - - wchar_t class_namew[100]; // (limited) buffer for Windows class name - - // convert UTF-8 class_name to wchar_t for RegisterClassExW and CreateWindowExW - - fl_utf8toUtf16(class_name, - (unsigned)strlen(class_name), // in - (unsigned short *)class_namew, // out - (unsigned)sizeof(class_namew) / sizeof(wchar_t)); // max. size - - if (!class_name_list.has_name(class_name)) { - WNDCLASSEXW wcw; - memset(&wcw, 0, sizeof(wcw)); - wcw.cbSize = sizeof(WNDCLASSEXW); - - // Documentation states a device context consumes about 800 bytes - // of memory... so who cares? If 800 bytes per window is what it - // takes to speed things up, I'm game. - wcw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; - wcw.lpfnWndProc = (WNDPROC)WndProc; - wcw.cbClsExtra = wcw.cbWndExtra = 0; - wcw.hInstance = fl_display; - if (!w->icon() && !icon_->count) - w->icon((void *)LoadIcon(NULL, IDI_APPLICATION)); - wcw.hIcon = wcw.hIconSm = (HICON)w->icon(); - wcw.hCursor = LoadCursor(NULL, IDC_ARROW); - wcw.hbrBackground = NULL; - wcw.lpszMenuName = NULL; - wcw.lpszClassName = class_namew; - RegisterClassExW(&wcw); - class_name_list.add_name(class_name); - } - - const wchar_t *message_namew = L"FLTK::ThreadWakeup"; - if (!fl_wake_msg) - fl_wake_msg = RegisterWindowMessageW(message_namew); - - HWND parent; - DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - DWORD styleEx = WS_EX_LEFT; - - // compute adequate screen where to put the window - int nscreen = 0; - if (w->parent()) { - nscreen = Fl_Window_Driver::driver(w->top_window())->screen_num(); - } else if (Fl_Window_Driver::driver(w)->force_position() && Fl_WinAPI_Window_Driver::driver(w)->screen_num_ >= 0) { - nscreen = Fl_Window_Driver::driver(w)->screen_num(); - } else { - Fl_Window *hint = Fl::first_window(); - if (hint) { - nscreen = Fl_Window_Driver::driver(hint->top_window())->screen_num(); - } else if (Fl::screen_driver()->screen_count() > 1 ) { - // put the new window on same screen as mouse - int mx, my, X, Y, W, H; - nscreen = Fl::screen_driver()->get_mouse(mx, my); - Fl::screen_xywh(X, Y, W, H, nscreen); - if (mx + w->w() >= X + W) mx = X + W - w->w(); - if (my + w->h() >= Y + H) my = Y + H - w->h(); - w->position(mx, my); - } - } - Fl_Window_Driver::driver(w)->screen_num(nscreen); - float s = Fl::screen_driver()->scale(nscreen); - int xp = int(round(w->x() * s)); // these are in graphical units - int yp = int(round(w->y() * s)); - int wp = int(w->w() * s); - int hp = int(w->h() * s); - - int showit = 1; - - if (w->parent()) { - style |= WS_CHILD; - styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; - parent = fl_xid(w->window()); - } else { // top level window - styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; - - int wintype = 0; - if (w->border()) { - if (is_resizable()) - wintype = 2; - else - wintype = 1; - } - - switch (wintype) { - // No border (used for menus) - case 0: - style |= WS_POPUP; - styleEx |= WS_EX_TOOLWINDOW; - break; - - // Thin border and title bar - case 1: - style |= WS_DLGFRAME | WS_CAPTION; - if (!w->modal()) - style |= WS_SYSMENU | WS_MINIMIZEBOX; - break; - - // Thick, resizable border and title bar, with maximize button - case 2: - style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION; - if (!w->modal()) - style |= WS_MINIMIZEBOX; - break; - } - - int xwm = xp, ywm = yp, bt, bx, by; // these are in graphical units - - fake_X_wm(xwm, ywm, bt, bx, by, style, styleEx); - - if (by + bt) { - wp += 2 * bx; - hp += 2 * by + bt; - } - if (!force_position()) { - xp = yp = CW_USEDEFAULT; - } else { - if (!Fl::grab()) { - xp = xwm; - yp = ywm; - x(int(round(xp / s))); - y(int(round(yp / s))); - } - xp -= bx; - yp -= by + bt; - } - - parent = 0; - if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) { - // find some other window to be "transient for": - Fl_Window *w = Fl_X::first->w; - while (w->parent()) - w = w->window(); - parent = fl_xid(w); - if (!w->visible()) - showit = 0; -// https://www.fltk.org/str.php?L1115 -// Mike added the code below to fix issues with tooltips that unfortunately -// he does not specify in detail. After extensive testing, I can't see -// how this fixes things, but I do see how a window opened by a timer will -// link that window to the current popup, which is wrong. -// Matt, Apr 30th, 2023 -// } else if (Fl::grab()) { -// parent = fl_xid(Fl::grab()); - } - } - - Fl_X *x = new Fl_X; - other_xid = 0; - x->w = w; - flx(x); - x->region = 0; - Fl_WinAPI_Window_Driver::driver(w)->private_dc = 0; - cursor = LoadCursor(NULL, IDC_ARROW); - custom_cursor = 0; - if (!fl_codepage) - fl_get_codepage(); - - WCHAR *lab = NULL; - if (w->label()) { - size_t l = strlen(w->label()); - unsigned wlen = fl_utf8toUtf16(w->label(), (unsigned)l, NULL, 0); // Pass NULL to query length - wlen++; - lab = (WCHAR *)malloc(sizeof(WCHAR) * wlen); - wlen = fl_utf8toUtf16(w->label(), (unsigned)l, (unsigned short *)lab, wlen); - lab[wlen] = 0; - } - x->xid = (fl_uintptr_t)CreateWindowExW(styleEx, - class_namew, lab, style, - xp, yp, wp, hp, - parent, - NULL, // menu - fl_display, - NULL // creation parameters - ); - if (lab) - free(lab); - - x->next = Fl_X::first; - Fl_X::first = x; - - set_icons(); - - if (w->fullscreen_active()) { - /* We need to make sure that the fullscreen is created on the - default monitor, ie the desktop where the shortcut is located - etc. This requires that CreateWindow is called with CW_USEDEFAULT - for x and y. We can then use GetWindowRect to determine which - monitor the window was placed on. */ - RECT rect; - GetWindowRect((HWND)x->xid, &rect); - make_fullscreen(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); - } - - // Setup clipboard monitor target if there are registered handlers and - // no window is targeted. - if (!fl_clipboard_notify_empty() && clipboard_wnd == NULL) - fl_clipboard_notify_target((HWND)x->xid); - - wait_for_expose_value = ((wp == 0 || hp == 0) && !w->border() && !w->parent() ? 0 : 1); // issue #985 - if (show_iconic()) { - showit = 0; - show_iconic(0); - } - if (showit) { - w->set_visible(); - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_SHOW); // get child windows to appear - Fl::e_number = old_event; - w->redraw(); // force draw to happen - } - - // Needs to be done before ShowWindow() to get the correct behavior - // when we get WM_SETFOCUS. - if (w->modal()) { - Fl::modal_ = w; - fl_fix_focus(); - } - - // If we've captured the mouse, we don't want to activate any - // other windows from the code, or we lose the capture. - ShowWindow((HWND)x->xid, !showit ? SW_SHOWMINNOACTIVE : - (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL); - - // Register all windows for potential drag'n'drop operations - RegisterDragDrop((HWND)x->xid, flIDropTarget); - - if (!im_enabled) - flImmAssociateContextEx((HWND)x->xid, 0, 0); - - if (w->fullscreen_active()) Fl::handle(FL_FULLSCREEN, w); -} - - -//////////////////////////////////////////////////////////////// - -HINSTANCE fl_display = GetModuleHandle(NULL); - -HINSTANCE fl_win32_display() { return fl_display; } - -void Fl_WinAPI_Window_Driver::set_minmax(LPMINMAXINFO minmax) { - int td, wd, hd, dummy_x, dummy_y; - - fake_X_wm(dummy_x, dummy_y, td, wd, hd); - wd *= 2; - hd *= 2; - hd += td; - - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - float s = Fl::screen_driver()->scale(screen_num()); - minmax->ptMinTrackSize.x = LONG(s * minw) + wd; - minmax->ptMinTrackSize.y = LONG(s * minh) + hd; - if (maxw) { - minmax->ptMaxTrackSize.x = LONG(s * maxw) + wd; - minmax->ptMaxSize.x = LONG(s * maxw) + wd; - } - if (maxh) { - minmax->ptMaxTrackSize.y = LONG(s * maxh) + hd; - minmax->ptMaxSize.y = LONG(s * maxh) + hd; - } -} - - -//////////////////////////////////////////////////////////////// - -// returns pointer to the filename, or null if name ends with '/' -const char *Fl_WinAPI_System_Driver::filename_name(const char *name) { - const char *p, *q; - if (!name) - return (0); - q = name; - if (q[0] && q[1] == ':') - q += 2; // skip leading drive letter - for (p = q; *p; p++) { - if (*p == '/' || *p == '\\') - q = p + 1; - } - return q; -} - - -//////////////////////////////////////////////////////////////// - -static HICON image_to_icon(const Fl_RGB_Image *image, bool is_icon, int hotx, int hoty) { - BITMAPV5HEADER bi; - HBITMAP bitmap, mask; - DWORD *bits; - HICON icon; - - if (!is_icon) { - if ((hotx < 0) || (hotx >= image->data_w())) - return NULL; - if ((hoty < 0) || (hoty >= image->data_h())) - return NULL; - } - - memset(&bi, 0, sizeof(BITMAPV5HEADER)); - - bi.bV5Size = sizeof(BITMAPV5HEADER); - bi.bV5Width = image->data_w(); - bi.bV5Height = -image->data_h(); // Negative for top-down - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x00FF0000; - bi.bV5GreenMask = 0x0000FF00; - bi.bV5BlueMask = 0x000000FF; - bi.bV5AlphaMask = 0xFF000000; - - HDC hdc; - - hdc = GetDC(NULL); - bitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&bits, NULL, 0); - ReleaseDC(NULL, hdc); - - if (bits == NULL) - return NULL; - - const uchar *i = (const uchar *)*image->data(); - const int extra_data = image->ld() ? (image->ld() - image->data_w() * image->d()) : 0; - - for (int y = 0; y < image->data_h(); y++) { - for (int x = 0; x < image->data_w(); x++) { - switch (image->d()) { - case 1: - *bits = (0xff << 24) | (i[0] << 16) | (i[0] << 8) | i[0]; - break; - case 2: - *bits = (i[1] << 24) | (i[0] << 16) | (i[0] << 8) | i[0]; - break; - case 3: - *bits = (0xff << 24) | (i[0] << 16) | (i[1] << 8) | i[2]; - break; - case 4: - *bits = (i[3] << 24) | (i[0] << 16) | (i[1] << 8) | i[2]; - break; - } - i += image->d(); - bits++; - } - i += extra_data; - } - - // A mask bitmap is still needed even though it isn't used - mask = CreateBitmap(image->data_w(), image->data_h(), 1, 1, NULL); - if (mask == NULL) { - DeleteObject(bitmap); - return NULL; - } - - ICONINFO ii; - - ii.fIcon = is_icon; - ii.xHotspot = hotx; - ii.yHotspot = hoty; - ii.hbmMask = mask; - ii.hbmColor = bitmap; - - icon = CreateIconIndirect(&ii); - - DeleteObject(bitmap); - DeleteObject(mask); - - return icon; -} - -//////////////////////////////////////////////////////////////// - -static HICON default_big_icon = NULL; -static HICON default_small_icon = NULL; - -static const Fl_RGB_Image *find_best_icon(int ideal_width, const Fl_RGB_Image *icons[], int count) { - const Fl_RGB_Image *best; - - best = NULL; - - for (int i = 0; i < count; i++) { - if (best == NULL) - best = icons[i]; - else { - if (best->w() < ideal_width) { - if (icons[i]->w() > best->w()) - best = icons[i]; - } else { - if ((icons[i]->w() >= ideal_width) && (icons[i]->w() < best->w())) - best = icons[i]; - } - } - } - - return best; -} - -void Fl_WinAPI_Screen_Driver::default_icons(const Fl_RGB_Image *icons[], int count) { - const Fl_RGB_Image *best_big, *best_small; - - if (default_big_icon != NULL) - DestroyIcon(default_big_icon); - if (default_small_icon != NULL) - DestroyIcon(default_small_icon); - - default_big_icon = NULL; - default_small_icon = NULL; - - best_big = find_best_icon(GetSystemMetrics(SM_CXICON), icons, count); - best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), icons, count); - - bool need_delete; - if (best_big != NULL) { - need_delete = false; - if (best_big->w() != best_big->data_w() || best_big->h() != best_big->data_h()) { - best_big = (Fl_RGB_Image *)best_big->copy(); - need_delete = true; - } - default_big_icon = image_to_icon(best_big, true, 0, 0); - if (need_delete) delete best_big; - } - - if (best_small != NULL) { - need_delete = false; - if (best_small->w() != best_small->data_w() || - best_small->h() != best_small->data_h()) { - best_small = (Fl_RGB_Image *)best_small->copy(); - need_delete = true; - } - default_small_icon = image_to_icon(best_small, true, 0, 0); - if (need_delete) delete best_small; - } -} - - -void Fl_Window::icons(HICON big_icon, HICON small_icon) { - free_icons(); - if (big_icon != NULL) - Fl_WinAPI_Window_Driver::driver(this)->icon_->big_icon = CopyIcon(big_icon); - if (small_icon != NULL) - Fl_WinAPI_Window_Driver::driver(this)->icon_->small_icon = CopyIcon(small_icon); - if (Fl_X::flx(this)) - Fl_WinAPI_Window_Driver::driver(this)->set_icons(); -} - -void Fl_Window::default_icons(HICON big_icon, HICON small_icon) { - if (default_big_icon != NULL) - DestroyIcon(default_big_icon); - if (default_small_icon != NULL) - DestroyIcon(default_small_icon); - - default_big_icon = NULL; - default_small_icon = NULL; - - if (big_icon != NULL) - default_big_icon = CopyIcon(big_icon); - if (small_icon != NULL) - default_small_icon = CopyIcon(small_icon); -} - -void Fl_WinAPI_Window_Driver::set_icons() { - HICON big_icon, small_icon; - - // Windows doesn't copy the icons, so we have to "leak" them when - // setting, and clean up when we change to some other icons. - big_icon = (HICON)SendMessage(fl_xid(pWindow), WM_GETICON, ICON_BIG, 0); - if ((big_icon != NULL) && (big_icon != default_big_icon)) - DestroyIcon(big_icon); - small_icon = (HICON)SendMessage(fl_xid(pWindow), WM_GETICON, ICON_SMALL, 0); - if ((small_icon != NULL) && (small_icon != default_small_icon)) - DestroyIcon(small_icon); - - big_icon = NULL; - small_icon = NULL; - - if (icon_->count) { - const Fl_RGB_Image *best_big, *best_small; - - best_big = find_best_icon(GetSystemMetrics(SM_CXICON), - (const Fl_RGB_Image **)icon_->icons, - icon_->count); - best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), - (const Fl_RGB_Image **)icon_->icons, - icon_->count); - - if (best_big != NULL) - big_icon = image_to_icon(best_big, true, 0, 0); - if (best_small != NULL) - small_icon = image_to_icon(best_small, true, 0, 0); - } else { - if ((icon_->big_icon != NULL) || (icon_->small_icon != NULL)) { - big_icon = icon_->big_icon; - small_icon = icon_->small_icon; - } else { - big_icon = default_big_icon; - small_icon = default_small_icon; - } - } - - SendMessage(fl_xid(pWindow), WM_SETICON, ICON_BIG, (LPARAM)big_icon); - SendMessage(fl_xid(pWindow), WM_SETICON, ICON_SMALL, (LPARAM)small_icon); -} - - -//////////////////////////////////////////////////////////////// - -#ifndef IDC_HAND -#define IDC_HAND MAKEINTRESOURCE(32649) -#endif // !IDC_HAND - -int Fl_WinAPI_Window_Driver::set_cursor(Fl_Cursor c) { - LPSTR n; - HCURSOR new_cursor; - - if (c == FL_CURSOR_NONE) - new_cursor = NULL; - else { - switch (c) { - case FL_CURSOR_ARROW: - n = IDC_ARROW; - break; - case FL_CURSOR_CROSS: - n = IDC_CROSS; - break; - case FL_CURSOR_WAIT: - n = IDC_WAIT; - break; - case FL_CURSOR_INSERT: - n = IDC_IBEAM; - break; - case FL_CURSOR_HAND: - n = IDC_HAND; - break; - case FL_CURSOR_HELP: - n = IDC_HELP; - break; - case FL_CURSOR_MOVE: - n = IDC_SIZEALL; - break; - case FL_CURSOR_N: - case FL_CURSOR_S: - // FIXME: Should probably have fallbacks for these instead - case FL_CURSOR_NS: - n = IDC_SIZENS; - break; - case FL_CURSOR_NE: - case FL_CURSOR_SW: - // FIXME: Dito. - case FL_CURSOR_NESW: - n = IDC_SIZENESW; - break; - case FL_CURSOR_E: - case FL_CURSOR_W: - // FIXME: Dito. - case FL_CURSOR_WE: - n = IDC_SIZEWE; - break; - case FL_CURSOR_SE: - case FL_CURSOR_NW: - // FIXME: Dito. - case FL_CURSOR_NWSE: - n = IDC_SIZENWSE; - break; - default: - return 0; - } - - new_cursor = LoadCursor(NULL, n); - if (new_cursor == NULL) - return 0; - } - - if ((cursor != NULL) && custom_cursor) - DestroyIcon(cursor); - - cursor = new_cursor; - custom_cursor = 0; - - SetCursor(cursor); - - return 1; -} - -int Fl_WinAPI_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { - HCURSOR new_cursor; - Fl_RGB_Image *scaled_image = (Fl_RGB_Image*)image->copy(); - scaled_image->normalize(); - new_cursor = image_to_icon(scaled_image, false, hotx, hoty); - delete scaled_image; - if (new_cursor == NULL) - return 0; - - if ((cursor != NULL) && custom_cursor) - DestroyIcon(cursor); - - cursor = new_cursor; - custom_cursor = 1; - - SetCursor(cursor); - - return 1; -} - - -//////////////////////////////////////////////////////////////// -// Implement the virtual functions for the base Fl_Window class: - -void Fl_WinAPI_Window_Driver::show() { - if (!shown()) { - makeWindow(); - } else { - // Once again, we would lose the capture if we activated the window. - Fl_X *i = Fl_X::flx(pWindow); - if (IsIconic((HWND)i->xid)) - OpenIcon((HWND)i->xid); - if (!fl_capture) - BringWindowToTop((HWND)i->xid); - // ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE); - } -} - -// the current context -// the current window handle, initially set to -1 so we can correctly -// allocate fl_GetDC(0) -HWND fl_window = NULL; - -// Here we ensure only one GetDC is ever in place. -HDC fl_GetDC(HWND w) { - HDC gc = (HDC)Fl_Graphics_Driver::default_driver().gc(); - if (gc) { - if (w == fl_window && fl_window != NULL) - return gc; - if (fl_window) - fl_release_dc(fl_window, gc); // ReleaseDC - } - gc = GetDC(w); - Fl_Graphics_Driver::default_driver().gc(gc); - fl_save_dc(w, gc); - fl_window = w; - // calling GetDC seems to always reset these: (?) - SetTextAlign(gc, TA_BASELINE | TA_LEFT); - SetBkMode(gc, TRANSPARENT); - - return gc; -} - - -/* Make sure that all allocated fonts are released. This works only if - Fl::run() is allowed to exit by closing all windows. Calling 'exit(int)' - will not automatically free any fonts. */ -void fl_free_fonts(void) { - // remove the Fl_Font_Descriptor chains - int i; - Fl_Fontdesc *s; - Fl_Font_Descriptor *f; - Fl_Font_Descriptor *ff; - for (i = 0; i < FL_FREE_FONT; i++) { - s = fl_fonts + i; - for (f = s->first; f; f = ff) { - ff = f->next; - delete (Fl_GDI_Font_Descriptor*)f; - s->first = ff; - } - } -} - - -/////////////////////////////////////////////////////////////////////// -// -// The following routines help fix a problem with the leaking of Windows -// Device Context (DC) objects. The 'proper' protocol is for a program to -// acquire a DC, save its state, do the modifications needed for drawing, -// perform the drawing, restore the initial state, and release the DC. In -// FLTK, the save and restore steps have previously been omitted and DCs are -// not properly released, leading to a great number of DC leaks. As some -// Windows "OSs" will hang when any process exceeds roughly 10,000 GDI objects, -// it is important to control GDI leaks, which are much more important than memory -// leaks. The following struct, global variable, and routines help implement -// the above protocol for those cases where the GetDC and RestoreDC are not in -// the same routine. For each GetDC, fl_save_dc is used to create an entry in -// a linked list that saves the window handle, the DC handle, and the initial -// state. When the DC is to be released, 'fl_release_dc' is called. It restores -// the initial state and releases the DC. When the program exits, 'fl_cleanup_dc_list' -// frees any remaining nodes in the list. - -struct Win_DC_List { // linked list - HWND window; // window handle - HDC dc; // device context handle - int saved_dc; // initial state of DC - Win_DC_List *next; // pointer to next item -}; - -static Win_DC_List *win_DC_list = 0; - -void fl_save_dc(HWND w, HDC dc) { - Win_DC_List *t; - t = new Win_DC_List; - t->window = w; - t->dc = dc; - t->saved_dc = SaveDC(dc); - if (win_DC_list) - t->next = win_DC_list; - else - t->next = NULL; - win_DC_list = t; -} - -void fl_release_dc(HWND w, HDC dc) { - Win_DC_List *t = win_DC_list; - Win_DC_List *prev = 0; - if (!t) - return; - do { - if (t->dc == dc) { - RestoreDC(dc, t->saved_dc); - ReleaseDC(w, dc); - if (!prev) { - win_DC_list = t->next; // delete first item - } else { - prev->next = t->next; // one in the middle - } - delete (t); - return; - } - prev = t; - t = t->next; - } while (t); -} - -void fl_cleanup_dc_list(void) { // clean up the list - Win_DC_List *t = win_DC_list; - if (!t) - return; - do { - RestoreDC(t->dc, t->saved_dc); - ReleaseDC(t->window, t->dc); - win_DC_list = t->next; - delete (t); - t = win_DC_list; - } while (t); -} - -/* Returns images of the captures of the window title-bar, and the left, bottom and right window borders. - This function exploits a feature of Fl_WinAPI_Screen_Driver::read_win_rectangle() which, - when fl_gc is set to the screen device context, captures the window decoration. - */ -void Fl_WinAPI_Window_Driver::capture_titlebar_and_borders(Fl_RGB_Image *&top, Fl_RGB_Image *&left, - Fl_RGB_Image *&bottom, Fl_RGB_Image *&right) { - top = left = bottom = right = NULL; - if (!shown() || parent() || !border() || !visible()) - return; - int wsides, hbottom, bt; - float scaling = Fl::screen_driver()->scale(screen_num()); - RECT r = border_width_title_bar_height(wsides, hbottom, bt); - int htop = bt + hbottom; - Fl_Surface_Device::push_current(Fl_Display_Device::display_device()); - pWindow->show(); - while (Fl::ready()) - Fl::check(); - HDC save_gc = (HDC)fl_graphics_driver->gc(); - fl_graphics_driver->gc(GetDC(NULL)); - int ww = int(w() * scaling) + 2 * wsides; - wsides = int(wsides / scaling); - if (wsides < 1) - wsides = 1; - ww = int(ww / scaling); - if (wsides <= 1) - ww = w() + 2 * wsides; - // capture the 4 window sides from screen - int offset = r.left < 0 ? -r.left : 0; - Fl_WinAPI_Screen_Driver *dr = (Fl_WinAPI_Screen_Driver *)Fl::screen_driver(); - if (htop && r.right - r.left > offset) { - top = dr->read_win_rectangle_unscaled(r.left+offset, r.top, r.right - r.left-offset, htop, 0); - if (scaling != 1 && top) - top->scale(ww, int(htop / scaling), 0, 1); - } - if (wsides) { - left = dr->read_win_rectangle_unscaled(r.left + offset, r.top + htop, wsides, int(h() * scaling), 0); - right = dr->read_win_rectangle_unscaled(r.right - wsides, r.top + htop, wsides, int(h() * scaling), 0); - bottom = dr->read_win_rectangle_unscaled(r.left+offset, r.bottom - hbottom, ww, hbottom, 0); - if (scaling != 1) { - if (left) left->scale(wsides, h(), 0, 1); - if (right) right->scale(wsides, h(), 0, 1); - if (bottom) bottom->scale(ww, hbottom, 0, 1); - } - } - ReleaseDC(NULL, (HDC)fl_graphics_driver->gc()); - fl_graphics_driver->gc(save_gc); - Fl_Surface_Device::pop_current(); -} diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index fce02de32..7da44acf2 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -62,7 +62,7 @@ #if FLTK_USE_CAIRO # include -# include +# include #endif // FLTK_USE_CAIRO #define USE_XRANDR (HAVE_DLSYM && HAVE_DLFCN_H) // means attempt to dynamically load libXrandr.so diff --git a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H index eee10900d..ca0cf5095 100644 --- a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H +++ b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H @@ -23,7 +23,7 @@ #include #include "../../Fl_Scalable_Graphics_Driver.H" // Fl_Font_Descriptor -#include +#include typedef struct _PangoLayout PangoLayout; typedef struct _PangoContext PangoContext; diff --git a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx index 236a677b1..bf2e2d361 100644 --- a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx +++ b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #if ! PANGO_VERSION_CHECK(1,16,0) # error "Requires Pango 1.16 or higher" diff --git a/src/drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx b/src/drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx index ed04d1167..a400460d8 100644 --- a/src/drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx +++ b/src/drivers/Cairo/Fl_X11_Cairo_Graphics_Driver.cxx @@ -20,7 +20,7 @@ #include "Fl_X11_Cairo_Graphics_Driver.H" #include -#include +#include #include #include diff --git a/src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.H b/src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.H deleted file mode 100644 index d5ba9c21c..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.H +++ /dev/null @@ -1,60 +0,0 @@ -// -// Class Fl_Cocoa_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#if HAVE_GL -#include -#include "../../Fl_Gl_Window_Driver.H" - -class Fl_Gl_Choice; -#ifdef __OBJC__ - @class NSOpenGLContext; -#else - class NSOpenGLContext; -#endif - -class Fl_Cocoa_Gl_Window_Driver : public Fl_Gl_Window_Driver { - NSOpenGLContext *gl1ctxt; // GL1 context in addition to GL3 context - friend Fl_Gl_Window_Driver* Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *); - Fl_Cocoa_Gl_Window_Driver(Fl_Gl_Window *win); - float pixels_per_unit() FL_OVERRIDE; - void before_show(int& need_after) FL_OVERRIDE; - void after_show() FL_OVERRIDE; - int mode_(int m, const int *a) FL_OVERRIDE; - void make_current_before() FL_OVERRIDE; - void swap_buffers() FL_OVERRIDE; - void resize(int is_a_resize, int w, int h) FL_OVERRIDE; - char swap_type() FL_OVERRIDE; - void swap_interval(int) FL_OVERRIDE; - int swap_interval() const FL_OVERRIDE; - Fl_Gl_Choice *find(int m, const int *alistp) FL_OVERRIDE; - GLContext create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) FL_OVERRIDE; - void set_gl_context(Fl_Window* w, GLContext context) FL_OVERRIDE; - void delete_gl_context(GLContext) FL_OVERRIDE; - void make_overlay_current() FL_OVERRIDE; - void redraw_overlay() FL_OVERRIDE; - void gl_start() FL_OVERRIDE; - char *alpha_mask_for_string(const char *str, int n, int w, int h, Fl_Fontsize fs) FL_OVERRIDE; - Fl_RGB_Image* capture_gl_rectangle(int x, int y, int w, int h) FL_OVERRIDE; - bool need_scissor() FL_OVERRIDE { return true; } - void* GetProcAddress(const char *procName) FL_OVERRIDE; - void apply_scissor(); - void switch_to_GL1() FL_OVERRIDE; - void switch_back() FL_OVERRIDE; -}; - - -#endif // HAVE_GL diff --git a/src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.mm b/src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.mm deleted file mode 100644 index 561aa2ce5..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Gl_Window_Driver.mm +++ /dev/null @@ -1,527 +0,0 @@ -// -// Class Fl_Cocoa_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#if HAVE_GL -#include -#include -#include "../../Fl_Gl_Choice.H" -#include "../../Fl_Screen_Driver.H" -#include "Fl_Cocoa_Window_Driver.H" -#include "Fl_Cocoa_Gl_Window_Driver.H" -#include -#include -#include -#include - -#import - -/* macOS offers only core contexts when using GL3. This forbids to draw - FLTK widgets in a GL3-using NSOpenGLContext because these widgets are drawn - with the GL1-based Fl_OpenGL_Graphics_Driver. The solution implemented here - is to create an additional NSView and an associated additional NSOpenGLContext - (gl1ctxt) placed above and sized as the GL3-based window, to set the new - NSOpenGLContext non opaque and GL1-based, and to draw the FLTK widgets in the - new view/GL1 context. - */ - -// Describes crap needed to create a GLContext. -class Fl_Cocoa_Gl_Choice : public Fl_Gl_Choice { - friend class Fl_Cocoa_Gl_Window_Driver; -private: - NSOpenGLPixelFormat* pixelformat; -public: - Fl_Cocoa_Gl_Choice(int m, const int *alistp, Fl_Gl_Choice *n) : Fl_Gl_Choice(m, alistp, n) { - pixelformat = NULL; - } -}; - - -Fl_Cocoa_Gl_Window_Driver::Fl_Cocoa_Gl_Window_Driver(Fl_Gl_Window *win) : - Fl_Gl_Window_Driver(win) { - gl1ctxt = NULL; -} - - -Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w) -{ - return new Fl_Cocoa_Gl_Window_Driver(w); -} - - -static NSOpenGLPixelFormat* mode_to_NSOpenGLPixelFormat(int m, const int *alistp) -{ - NSOpenGLPixelFormatAttribute attribs[32]; - int n = 0; - // AGL-style code remains commented out for comparison - if (!alistp) { - if (m & FL_INDEX) { - //list[n++] = AGL_BUFFER_SIZE; list[n++] = 8; - } else { - //list[n++] = AGL_RGBA; - //list[n++] = AGL_GREEN_SIZE; - //list[n++] = (m & FL_RGB8) ? 8 : 1; - attribs[n++] = NSOpenGLPFAColorSize; - attribs[n++] = (NSOpenGLPixelFormatAttribute)((m & FL_RGB8) ? 32 : 1); - if (m & FL_ALPHA) { - //list[n++] = AGL_ALPHA_SIZE; - attribs[n++] = NSOpenGLPFAAlphaSize; - attribs[n++] = (NSOpenGLPixelFormatAttribute)((m & FL_RGB8) ? 8 : 1); - } - if (m & FL_ACCUM) { - //list[n++] = AGL_ACCUM_GREEN_SIZE; list[n++] = 1; - attribs[n++] = NSOpenGLPFAAccumSize; - attribs[n++] = (NSOpenGLPixelFormatAttribute)1; - if (m & FL_ALPHA) { - //list[n++] = AGL_ACCUM_ALPHA_SIZE; list[n++] = 1; - } - } - } - if (m & FL_DOUBLE) { - //list[n++] = AGL_DOUBLEBUFFER; - attribs[n++] = NSOpenGLPFADoubleBuffer; - } - if (m & FL_DEPTH32) { - //list[n++] = AGL_DEPTH_SIZE; list[n++] = 32; - attribs[n++] = NSOpenGLPFADepthSize; - attribs[n++] = (NSOpenGLPixelFormatAttribute)32; - } else if (m & FL_DEPTH) { - //list[n++] = AGL_DEPTH_SIZE; list[n++] = 24; - attribs[n++] = NSOpenGLPFADepthSize; - attribs[n++] = (NSOpenGLPixelFormatAttribute)24; - } - if (m & FL_STENCIL) { - //list[n++] = AGL_STENCIL_SIZE; list[n++] = 1; - attribs[n++] = NSOpenGLPFAStencilSize; - attribs[n++] = (NSOpenGLPixelFormatAttribute)1; - } - if (m & FL_STEREO) { - //list[n++] = AGL_STEREO; - attribs[n++] = 6/*NSOpenGLPFAStereo*/; - } - if (m & FL_MULTISAMPLE) { - attribs[n++] = NSOpenGLPFAMultisample; // 10.4 - attribs[n++] = NSOpenGLPFASampleBuffers; attribs[n++] = (NSOpenGLPixelFormatAttribute)1; - attribs[n++] = NSOpenGLPFASamples; attribs[n++] = (NSOpenGLPixelFormatAttribute)4; - } - attribs[n++] = NSOpenGLPFAOpenGLProfile; - attribs[n++] = (m & FL_OPENGL3) ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy; - } else { - while (alistp[n] && n < 30) { - attribs[n] = (NSOpenGLPixelFormatAttribute)alistp[n]; - n++; - } - } - attribs[n] = (NSOpenGLPixelFormatAttribute)0; - NSOpenGLPixelFormat *pixform = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; - /*GLint color,alpha,accum,depth; - [pixform getValues:&color forAttribute:NSOpenGLPFAColorSize forVirtualScreen:0]; - [pixform getValues:&alpha forAttribute:NSOpenGLPFAAlphaSize forVirtualScreen:0]; - [pixform getValues:&accum forAttribute:NSOpenGLPFAAccumSize forVirtualScreen:0]; - [pixform getValues:&depth forAttribute:NSOpenGLPFADepthSize forVirtualScreen:0]; - NSLog(@"color=%d alpha=%d accum=%d depth=%d",color,alpha,accum,depth);*/ - return pixform; -} - - -Fl_Gl_Choice *Fl_Cocoa_Gl_Window_Driver::find(int m, const int *alistp) -{ - Fl::screen_driver()->open_display(); // useful when called through gl_start() - Fl_Cocoa_Gl_Choice *g = (Fl_Cocoa_Gl_Choice*)Fl_Gl_Window_Driver::find_begin(m, alistp); - if (g) return g; - NSOpenGLPixelFormat* fmt = mode_to_NSOpenGLPixelFormat(m, alistp); - if (!fmt) return 0; - g = new Fl_Cocoa_Gl_Choice(m, alistp, first); - first = g; - g->pixelformat = fmt; - return g; -} - - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -# define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity -#endif - -static void remove_gl_context_opacity(NSOpenGLContext *ctx) { - GLint gl_opacity; - [ctx getValues:&gl_opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; - if (gl_opacity != 0) { - gl_opacity = 0; - [ctx setValues:&gl_opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; - } -} - - -static NSOpenGLContext *create_GLcontext_for_window( - NSOpenGLPixelFormat *pixelformat, - NSOpenGLContext *shared_ctx, Fl_Window *window) -{ - NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelformat shareContext:shared_ctx]; - if (shared_ctx && !context) context = [[NSOpenGLContext alloc] initWithFormat:pixelformat shareContext:nil]; - if (context) { - NSView *view = [fl_xid(window) contentView]; - [view setWantsBestResolutionOpenGLSurface:(Fl::use_high_res_GL() != 0)]; - [context setView:view]; - if (Fl_Cocoa_Window_Driver::driver(window)->subRect()) { - remove_gl_context_opacity(context); - } - } - return context; -} - -GLContext Fl_Cocoa_Gl_Window_Driver::create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) { - GLContext context, shared_ctx = 0; - if (context_list && nContext) shared_ctx = context_list[0]; - // resets the pile of string textures used to draw strings - // necessary before the first context is created - if (!shared_ctx) gl_texture_reset(); - context = create_GLcontext_for_window(((Fl_Cocoa_Gl_Choice*)g)->pixelformat, (NSOpenGLContext*)shared_ctx, window); - if (!context) return 0; - add_context(context); - [(NSOpenGLContext*)context makeCurrentContext]; - glClearColor(0., 0., 0., 1.); - apply_scissor(); - return (context); -} - -void Fl_Cocoa_Gl_Window_Driver::set_gl_context(Fl_Window* w, GLContext context) { - NSOpenGLContext *current_context = [NSOpenGLContext currentContext]; - if (context != current_context || w != cached_window) { - cached_window = w; - [(NSOpenGLContext*)context makeCurrentContext]; - } -} - -void Fl_Cocoa_Gl_Window_Driver::delete_gl_context(GLContext context) { - NSOpenGLContext *current_context = [NSOpenGLContext currentContext]; - if (current_context == context) { - cached_window = 0; - [current_context clearDrawable]; - } - [(NSOpenGLContext*)context release]; - del_context(context); - if (gl1ctxt) { - [[gl1ctxt view] release]; - [gl1ctxt release]; - gl1ctxt = 0; - } -} - -void Fl_Cocoa_Gl_Window_Driver::make_overlay_current() { - // this is not very useful, but unfortunately, Apple decided - // that front buffer drawing can no longer (OS X 10.4) be supported on their platforms. - if (pWindow->shown()) pWindow->make_current(); -} - -void Fl_Cocoa_Gl_Window_Driver::redraw_overlay() { - pWindow->redraw(); -} - -void Fl_Cocoa_Gl_Window_Driver::before_show(int& need_after) { - need_after = 1; -} - -void Fl_Cocoa_Gl_Window_Driver::after_show() { - // Makes sure the GL context is created to avoid drawing twice the window when first shown - pWindow->make_current(); - if ((mode() & FL_OPENGL3) && !gl1ctxt) { - // Create transparent GL1 scene above the GL3 scene to hold child widgets and/or text - NSView *view = [fl_mac_xid(pWindow) contentView]; - NSView *gl1view = [[NSView alloc] initWithFrame:[view frame]]; - [gl1view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; - static NSOpenGLContext *shared_gl1_ctxt = nil; - static NSOpenGLPixelFormat *gl1pixelformat = mode_to_NSOpenGLPixelFormat( - FL_RGB8 | FL_ALPHA | FL_SINGLE, NULL); - gl1ctxt = [[NSOpenGLContext alloc] initWithFormat:gl1pixelformat shareContext:shared_gl1_ctxt]; - if (!shared_gl1_ctxt) { - shared_gl1_ctxt = gl1ctxt; - [shared_gl1_ctxt retain]; - } - [view addSubview:gl1view]; - if (Fl::use_high_res_GL()) { - [gl1view setWantsBestResolutionOpenGLSurface:YES]; - } - [gl1ctxt setView:gl1view]; - remove_gl_context_opacity(gl1ctxt); - } -} - -float Fl_Cocoa_Gl_Window_Driver::pixels_per_unit() -{ - int retina = (Fl::use_high_res_GL() && Fl_X::flx(pWindow) && - Fl_Cocoa_Window_Driver::driver(pWindow)->mapped_to_retina()) ? 2 : 1; - return retina * Fl_Graphics_Driver::default_driver().scale(); -} - -int Fl_Cocoa_Gl_Window_Driver::mode_(int m, const int *a) { - if (a) { // when the mode is set using the a array of system-dependent values, and if asking for double buffer, - // the FL_DOUBLE flag must be set in the mode_ member variable - const int *aa = a; - while (*aa) { - if (*(aa++) == - kCGLPFADoubleBuffer - ) { m |= FL_DOUBLE; break; } - } - } - pWindow->context(0); - mode( m); alist(a); - if (pWindow->shown()) { - g( find(m, a) ); - pWindow->redraw(); - } else { - g(0); - } - return 1; -} - -void Fl_Cocoa_Gl_Window_Driver::make_current_before() { - // detect if the window was moved between low and high resolution displays - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(pWindow); - if (d->changed_resolution()){ - d->changed_resolution(false); - pWindow->invalidate(); - [(NSOpenGLContext*)pWindow->context() update]; - } -} - -void Fl_Cocoa_Gl_Window_Driver::swap_buffers() { - if (overlay() != NULL) { - // STR# 2944 [1] - // Save matrixmode/proj/modelview/rasterpos before doing overlay. - // - int wo = pWindow->pixel_w(), ho = pWindow->pixel_h(); - GLint matrixmode; - GLfloat pos[4]; - glGetIntegerv(GL_MATRIX_MODE, &matrixmode); - glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); // save original glRasterPos - glMatrixMode(GL_PROJECTION); // save proj/model matrices - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glScalef(2.0f/wo, 2.0f/ho, 1.0f); - glTranslatef(-wo/2.0f, -ho/2.0f, 0.0f); // set transform so 0,0 is bottom/left of Gl_Window - glRasterPos2i(0,0); // set glRasterPos to bottom left corner - { - // Emulate overlay by doing copypixels - glReadBuffer(GL_BACK); - glDrawBuffer(GL_FRONT); - glCopyPixels(0, 0, wo, ho, GL_COLOR); // copy GL_BACK to GL_FRONT - } - glPopMatrix(); // GL_MODELVIEW // restore model/proj matrices - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(matrixmode); - glRasterPos3f(pos[0], pos[1], pos[2]); // restore original glRasterPos - } else { - [(NSOpenGLContext*)pWindow->context() flushBuffer]; - } -} - -char Fl_Cocoa_Gl_Window_Driver::swap_type() {return copy;} - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -# define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval -#endif - -void Fl_Cocoa_Gl_Window_Driver::swap_interval(int n) { - GLint interval = (GLint)n; - NSOpenGLContext* ctx = (NSOpenGLContext*)pWindow->context(); - if (ctx) - [ctx setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval]; -} - -int Fl_Cocoa_Gl_Window_Driver::swap_interval() const { - GLint interval = (GLint)-1; - NSOpenGLContext* ctx = (NSOpenGLContext*)pWindow->context(); - if (ctx) - [ctx getValues:&interval forParameter:NSOpenGLContextParameterSwapInterval]; - return interval; -} - -void Fl_Cocoa_Gl_Window_Driver::resize(int is_a_resize, int w, int h) { - if (pWindow->shown()) apply_scissor(); - [(NSOpenGLContext*)pWindow->context() update]; - if (gl1ctxt) { - [gl1ctxt update]; - } -} - -void Fl_Cocoa_Gl_Window_Driver::apply_scissor() { - if (glIsEnabled(GL_SCISSOR_TEST)) glDisable(GL_SCISSOR_TEST); - CGRect *extents = Fl_Cocoa_Window_Driver::driver(pWindow)->subRect(); - if (extents) { - remove_gl_context_opacity((NSOpenGLContext*)pWindow->context()); - GLdouble vals[4]; - glGetDoublev(GL_COLOR_CLEAR_VALUE, vals); - glClearColor(0., 0., 0., 0.); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(vals[0], vals[1], vals[2], vals[3]); - float s = pWindow->pixels_per_unit(); - glScissor(s*extents->origin.x, s*extents->origin.y, s*extents->size.width, s*extents->size.height); -//printf("apply_scissor %dx%d %dx%d\n",extents->x, extents->y, extents->width, extents->height); - glEnable(GL_SCISSOR_TEST); - } -} - - -/* Some old Apple hardware doesn't implement the GL_EXT_texture_rectangle extension. - For it, draw_string_legacy_glut() is used to draw text. */ - -char *Fl_Cocoa_Gl_Window_Driver::alpha_mask_for_string(const char *str, int n, int w, int h, Fl_Fontsize fs) -{ - // write str to a bitmap just big enough - Fl_Image_Surface *surf = new Fl_Image_Surface(w, h); - Fl_Font f=fl_font(); - Fl_Surface_Device::push_current(surf); - fl_color(FL_WHITE); - fl_font(f, fs); - fl_draw(str, n, 0, fl_height() - fl_descent()); - // get the alpha channel only of the bitmap - char *alpha_buf = new char[w*h], *r = alpha_buf, *q; - q = (char*)CGBitmapContextGetData((CGContextRef)surf->offscreen()); - for (int i = 0; i < h; i++) { - for (int j = 0; j < w; j++) { - *r++ = *(q+3); - q += 4; - } - } - Fl_Surface_Device::pop_current(); - delete surf; - return alpha_buf; -} - -void Fl_Cocoa_Gl_Window_Driver::gl_start() { - [(NSOpenGLContext*)gl_start_context update]; -} - -// convert BGRA to RGB and also exchange top and bottom -static uchar *convert_BGRA_to_RGB(uchar *baseAddress, int w, int h, int mByteWidth) -{ - uchar *newimg = new uchar[3*w*h]; - uchar *to = newimg; - for (int i = h-1; i >= 0; i--) { - uchar *from = baseAddress + i * mByteWidth; - for (int j = 0; j < w; j++, from += 4) { -#if defined(__ppc__) && __ppc__ - memcpy(to, from + 1, 3); - to += 3; -#else - *(to++) = *(from+2); - *(to++) = *(from+1); - *(to++) = *from; -#endif - } - } - delete[] baseAddress; - return newimg; -} - - -static Fl_RGB_Image *cgimage_to_rgb4(CGImageRef img) { - int w = (int)CGImageGetWidth(img); - int h = (int)CGImageGetHeight(img); - CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); - uchar *rgba = new uchar[4 * w * h]; - CGContextRef auxgc = CGBitmapContextCreate(rgba, w, h, 8, 4 * w, cspace, - kCGImageAlphaPremultipliedLast); - CGColorSpaceRelease(cspace); - CGContextDrawImage(auxgc, CGRectMake(0, 0, w, h), img); - CGContextRelease(auxgc); - Fl_RGB_Image *rgb = new Fl_RGB_Image(rgba, w, h, 4); - rgb->alloc_array = 1; - return rgb; -} - - -Fl_RGB_Image* Fl_Cocoa_Gl_Window_Driver::capture_gl_rectangle(int x, int y, int w, int h) -{ - Fl_Gl_Window* glw = pWindow; - float factor = glw->pixels_per_unit(); - if (factor != 1) { - w *= factor; h *= factor; x *= factor; y *= factor; - } - NSWindow *nswin = (NSWindow*)fl_mac_xid(pWindow); - CGImageRef img_full = Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(nswin); - int bt = [nswin frame].size.height - [[nswin contentView] frame].size.height; - bt *= (factor / Fl_Graphics_Driver::default_driver().scale()); - CGRect cgr = CGRectMake(x, y + bt, w, h); // add vertical offset to bypass titlebar - CGImageRef cgimg = CGImageCreateWithImageInRect(img_full, cgr); // 10.4 - CGImageRelease(img_full); - Fl_RGB_Image *rgb = cgimage_to_rgb4(cgimg); - CGImageRelease(cgimg); - return rgb; - [(NSOpenGLContext*)glw->context() makeCurrentContext]; - // to capture also the overlay and for directGL demo - [(NSOpenGLContext*)glw->context() flushBuffer]; - // Read OpenGL context pixels directly. - // For extra safety, save & restore OpenGL states that are changed - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - glPixelStorei(GL_PACK_ALIGNMENT, 4); /* Force 4-byte alignment */ - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glPixelStorei(GL_PACK_SKIP_ROWS, 0); - glPixelStorei(GL_PACK_SKIP_PIXELS, 0); - // Read a block of pixels from the frame buffer - int mByteWidth = w * 4; - mByteWidth = (mByteWidth + 3) & ~3; // Align to 4 bytes - uchar *baseAddress = new uchar[mByteWidth * h]; - glReadPixels(x, glw->pixel_h() - (y+h), w, h, - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, baseAddress); - glPopClientAttrib(); - baseAddress = convert_BGRA_to_RGB(baseAddress, w, h, mByteWidth); - Fl_RGB_Image *img = new Fl_RGB_Image(baseAddress, w, h, 3, 3 * w); - img->alloc_array = 1; - [(NSOpenGLContext*)glw->context() flushBuffer]; - return img; -} - - -void* Fl_Cocoa_Gl_Window_Driver::GetProcAddress(const char *procName) { - return dlsym(RTLD_DEFAULT, procName); -} - - -FL_EXPORT NSOpenGLContext *fl_mac_glcontext(GLContext rc) { - return (NSOpenGLContext*)rc; -} - - -void Fl_Cocoa_Gl_Window_Driver::switch_to_GL1() { - [gl1ctxt makeCurrentContext]; - glClearColor(0., 0., 0., 0.); - glClear(GL_COLOR_BUFFER_BIT); -} - - -void Fl_Cocoa_Gl_Window_Driver::switch_back() { - glFlush(); - [(NSOpenGLContext*)pWindow->context() makeCurrentContext]; -} - - -class Fl_Gl_Cocoa_Plugin : public Fl_Cocoa_Plugin { -public: - Fl_Gl_Cocoa_Plugin() : Fl_Cocoa_Plugin(name()) { } - const char *name() FL_OVERRIDE { return "gl.cocoa.fltk.org"; } - void resize(Fl_Gl_Window *glw, int x, int y, int w, int h) FL_OVERRIDE { - glw->Fl_Gl_Window::resize(x, y, w, h); - } -}; - -static Fl_Gl_Cocoa_Plugin Gl_Cocoa_Plugin; - -#endif // HAVE_GL diff --git a/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm b/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm deleted file mode 100644 index 9c75c7ff2..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Pen_Events.mm +++ /dev/null @@ -1,439 +0,0 @@ -// -// Definition of macOS Cocoa Pen/Tablet event driver. -// -// Copyright 2025-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "src/drivers/Base/Fl_Base_Pen_Events.H" - -#include -#include -#include -#include "../../Fl_Screen_Driver.H" - -#import - - -extern Fl_Window *fl_xmousewin; - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - -typedef short NSEventSubtype; -#define NSPointingDeviceTypePen NSPenPointingDevice -#define NSEventTypeMouseEntered NSMouseEntered -#define NSEventTypeMouseExited NSMouseExited -#define NSEventTypeTabletProximity NSTabletProximity -#define NSEventTypeTabletPoint NSTabletPoint -#define NSEventSubtypeTabletProximity NSTabletProximityEventSubtype -#define NSEventSubtypeTabletPoint NSTabletPointEventSubtype -#define NSEventSubtypeMouseEvent NSMouseEventSubtype -#define NSEventTypeLeftMouseDown NSLeftMouseDown -#define NSEventTypeLeftMouseUp NSLeftMouseUp -#define NSEventTypeLeftMouseDragged NSLeftMouseDragged -#define NSEventTypeMouseMoved NSMouseMoved -#define NSEventTypeRightMouseDown NSRightMouseDown -#define NSEventTypeRightMouseUp NSRightMouseUp -#define NSEventTypeRightMouseDragged NSRightMouseDragged -#define NSEventTypeOtherMouseUp NSOtherMouseUp -#define NSEventTypeOtherMouseDown NSOtherMouseDown -#define NSEventTypeOtherMouseDragged NSOtherMouseDragged -#define NSPointingDeviceTypeEraser NSEraserPointingDevice - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - -static NSPointingDeviceType device_type_ { NSPointingDeviceTypePen }; - -// The trait list keeps track of traits for every pen ID that appears while -// handling events. -// AppKit does not tell us what traits are available per pen or tablet, so -// we use the first 5 motion events to discover event values that are not -// the default value, and enter that knowledge into the traits database. -typedef std::map TraitList; -static TraitList trait_list_; -static int trait_countdown_ { 5 }; -static int current_pen_id_ { -1 }; -static Fl::Pen::Trait current_pen_trait_ { Fl::Pen::Trait::DRIVER_AVAILABLE }; -static Fl::Pen::Trait driver_traits_ { - Fl::Pen::Trait::DRIVER_AVAILABLE | Fl::Pen::Trait::PEN_ID | - Fl::Pen::Trait::ERASER | Fl::Pen::Trait::PRESSURE | - Fl::Pen::Trait::BARREL_PRESSURE | Fl::Pen::Trait::TILT_X | - Fl::Pen::Trait::TILT_Y | Fl::Pen::Trait::TWIST - // Notably missing: PROXIMITY -}; - -// Temporary storage of event data for the driver; -static Fl::Pen::EventData ev; - - -namespace Fl { - -namespace Private { - -// Global mouse position at mouse down event -extern int e_x_down; -extern int e_y_down; - -}; // namespace Private - -namespace Pen { - -class Cocoa_Driver : public Driver { -public: - Cocoa_Driver() = default; - //virtual void subscribe(Fl_Widget* widget) override; - //virtual void unsubscribe(Fl_Widget* widget) override; - //virtual void release() override; - virtual Trait traits() override { return driver_traits_; } - virtual Trait pen_traits(int pen_id) override { - auto it = trait_list_.find(pen_id); - if (pen_id == 0) - return current_pen_trait_; - if (it == trait_list_.end()) { - return Trait::DRIVER_AVAILABLE; - } else { - return it->second; - } - } -}; - -Cocoa_Driver cocoa_driver; -Driver& driver = cocoa_driver; - -} // namespace Pen - -} // namespace Fl - - -using namespace Fl::Pen; - - -/* - Copy the event state. - */ -static void copy_state() { - Fl::Pen::State tr = (Fl::Pen::State)((uint32_t)Fl::Pen::e.state ^ (uint32_t)ev.state); - Fl::Pen::e = ev; - Fl::Pen::e.trigger = tr; - Fl::e_x = (int)ev.x; - Fl::e_y = (int)ev.y; - Fl::e_x_root = (int)ev.rx; - Fl::e_y_root = (int)ev.ry; -} - -/* - Offset coordinates for subwindows and subsubwindows. - */ -static void offset_subwindow_event(Fl_Widget *w, double &x, double &y) { - Fl_Widget *p = w, *q; - while (p) { - q = p->parent(); - if (p->as_window() && q) { - x -= p->x(); - y -= p->y(); - } - p = q; - }; -} - -/* - Check if coordinates are within the widget box. - Coordinates are in top_window space. We iterate up the hierarchy to ensure - that we handle subwindows correctly. - */ -static bool event_inside(Fl_Widget *w, double x, double y) { - offset_subwindow_event(w, x, y); - if (w->as_window()) { - return ((x >= 0) && (y >= 0) && (x < w->w()) && (y < w->h())); - } else { - return ((x >= w->x()) && (y >= w->y()) && (x < w->x() + w->w()) && (y < w->y() + w->h())); - } -} - -/* - Find the widget under the pen event. - Search the subscriber list for widgets that are inside the same top window, - are visible, and are within the give coordinates. Subwindow aware. - */ -static Fl_Widget *find_below_pen(Fl_Window *win, double x, double y) { - for (auto &sub: subscriber_list_) { - Fl_Widget *candidate = sub.second->widget(); - if (candidate && (candidate->top_window() == win)) { - if (candidate->visible() && event_inside(candidate, x, y)) { - return candidate; - } - } - } - return nullptr; -} - -/* - Send the current event and event data to a widget. - Note: we will get the wrong coordinates if the widget is not a child of - the current event window (LEAVE events between windows). - */ -static int pen_send(Fl_Widget *w, int event, State trigger, bool &copied) { - // Copy most event data only once - if (!copied) { - copy_state(); - copied = true; - } - // Copy the top_window coordinates again as they may change when w changes - e.x = ev.x; - e.y = ev.y; - offset_subwindow_event(w, e.x, e.y); - Fl::e_x = e.x; - Fl::e_y = e.y; - // Send the event. - e.trigger = trigger; - return w->handle(event); -} - -/* - Send an event to all subscribers. - */ -static int pen_send_all(int event, State trigger) { - bool copied = false; - // use local value because handler may still change ev values - for (auto &it: subscriber_list_) { - auto w = it.second->widget(); - if (w) - pen_send(w, event, trigger, copied); - } - return 1; -} - -/* - Convert the NSEvent button number to Fl::Pen::State, - */ -static State button_to_trigger(NSInteger button, bool down) { - switch (button) { - case 0: - if ( (ev.state & (State::ERASER_DOWN | State::ERASER_HOVERS)) != State::NONE ) { - return down ? State::ERASER_DOWN : State::ERASER_HOVERS; - } else { - return down ? State::TIP_DOWN : State::TIP_HOVERS; - } - case 1: return State::BUTTON0; - case 2: return State::BUTTON1; - case 3: return State::BUTTON2; - case 4: return State::BUTTON3; - default: return State::NONE; - } -} - -/* - Handle events coming from Cocoa. - `capabilityMask` is useless, because it is vendor defined - If a modal window is open, AppKit will send window specific events only there. - */ -bool fl_cocoa_tablet_handler(NSEvent *event, Fl_Window *eventWindow) { - // Quick access to the main type. - auto type = [event type]; - - // There seems nothing useful here. Ignore for now. - if ((type == NSEventTypeMouseEntered) || (type == NSEventTypeMouseExited)) { - return false; - } - - // Sort out tablet-only events and mouse plus tablet events. - bool is_mouse = ((type != NSEventTypeTabletPoint) && (type != NSEventTypeTabletProximity)); - - // Set the subtype if one is available. Only NSEventSubtypeTabletPoint and - // NSEventSubtypeTabletProximity matter in this context - NSEventSubtype subtype = is_mouse ? [event subtype] : NSEventSubtypeMouseEvent; - - // Is this a change in proximity event? - bool is_proximity = ((type == NSEventTypeTabletProximity) || (subtype == NSEventSubtypeTabletProximity)); - - // Is this a pen pointer event? - bool is_point = ((type == NSEventTypeTabletPoint) || (subtype == NSEventSubtypeTabletPoint)); - - // Check if any of the pen down, move, drag, or up events was triggered. - bool is_down = ((type == NSEventTypeLeftMouseDown) || (type == NSEventTypeRightMouseDown) || (type == NSEventTypeOtherMouseDown)); - bool is_up = ((type == NSEventTypeLeftMouseUp) || (type == NSEventTypeRightMouseUp) || (type == NSEventTypeOtherMouseUp)); - bool is_drag = ((type == NSEventTypeLeftMouseDragged) || (type == NSEventTypeRightMouseDragged) || (type == NSEventTypeOtherMouseDragged)); - bool is_motion = is_drag || (type == NSEventTypeMouseMoved); - - // Find out if we can get the pen position - bool has_position = (eventWindow != nullptr) && (is_up || is_down || is_motion || is_proximity || is_point); - - // Event has extended pen data set: - if (has_position) { - // Get the position data. - auto pt = [event locationInWindow]; - double s = Fl::screen_driver()->scale(0); - ev.x = pt.x/s; - ev.y = eventWindow->h() - pt.y/s; - ev.rx = ev.x + eventWindow->x(); - ev.ry = ev.y + eventWindow->y(); - if (!is_proximity) { - // Get the pressure data. - ev.pressure = [event pressure]; - ev.barrel_pressure = [event tangentialPressure]; - // Get the tilt - auto tilt = [event tilt]; - ev.tilt_x = -tilt.x; - ev.tilt_y = tilt.y; - // Other stuff - ev.twist = [event rotation]; // TODO: untested - // ev.proximity = [event proximity]; // not supported in AppKit - } - if (device_type_ == NSPointingDeviceTypeEraser) { - if ([event buttonMask] & 1) - ev.state = State::ERASER_DOWN; - else - ev.state = State::ERASER_HOVERS; - } else { - if ([event buttonMask] & 1) - ev.state = State::TIP_DOWN; - else - ev.state = State::TIP_HOVERS; - } - if ([event buttonMask] & 0x0002) ev.state |= State::BUTTON0; - if ([event buttonMask] & 0x0004) ev.state |= State::BUTTON1; - if ([event buttonMask] & 0x0008) ev.state |= State::BUTTON2; - if ([event buttonMask] & 0x0010) ev.state |= State::BUTTON3; - // printf("0x%08x\n", [event buttonMask]); - } - if (is_proximity) { - ev.pen_id = (int)[event vendorID]; - device_type_ = [event pointingDeviceType]; - } - if (type == NSEventTypeTabletProximity) { - if ([event isEnteringProximity]) { - // Check if this is the first time we see this pen, or if the pen changed - if (current_pen_id_ != ev.pen_id) { - current_pen_id_ = ev.pen_id; - auto it = trait_list_.find(current_pen_id_); - if (it == trait_list_.end()) { // not found, create a new entry - trait_list_[current_pen_id_] = Trait::DRIVER_AVAILABLE; - trait_countdown_ = 5; - pen_send_all(Fl::Pen::DETECTED, State::NONE); - // printf("IN RANGE, NEW PEN\n"); - } else { - pen_send_all(Fl::Pen::CHANGED, State::NONE); - // printf("IN RANGE, CHANGED PEN\n"); - } - trait_list_[0] = trait_list_[current_pen_id_]; // set current pen traits - } else { - pen_send_all(Fl::Pen::IN_RANGE, State::NONE); - // printf("IN RANGE\n"); - } - } else { - pen_send_all(Fl::Pen::OUT_OF_RANGE, State::NONE); - // printf("OUT OF RANGE\n"); - } - } - - Fl_Widget *receiver = nullptr; - bool pushed = false; - bool event_data_copied = false; - - if (has_position) { - if (trait_countdown_) { - trait_countdown_--; - if (ev.tilt_x != 0.0) current_pen_trait_ |= Trait::TILT_X; - if (ev.tilt_y != 0.0) current_pen_trait_ |= Trait::TILT_Y; - if (ev.pressure != 1.0) current_pen_trait_ |= Trait::PRESSURE; - if (ev.barrel_pressure != 0.0) current_pen_trait_ |= Trait::BARREL_PRESSURE; - if (ev.pen_id != 0) current_pen_trait_ |= Trait::PEN_ID; - if (ev.twist != 0.0) current_pen_trait_ |= Trait::TWIST; - //if (ev.proximity != 0) current_pen_trait_ |= Trait::PROXIMITY; - trait_list_[current_pen_id_] = current_pen_trait_; - } - fl_xmousewin = eventWindow; - if (pushed_ && pushed_->widget() && (Fl::pushed() == pushed_->widget())) { - receiver = pushed_->widget(); - if (Fl::grab() && (Fl::grab() != receiver->top_window())) - return 0; - if (Fl::modal() && (Fl::modal() != receiver->top_window())) - return 0; - pushed = true; - } else { - if (Fl::grab() && (Fl::grab() != eventWindow)) - return 0; - if (Fl::modal() && (Fl::modal() != eventWindow)) - return 0; - auto bpen = below_pen_ ? below_pen_->widget() : nullptr; - auto bmouse = Fl::belowmouse(); - auto bpen_old = bmouse && (bmouse == bpen) ? bpen : nullptr; - auto bpen_now = find_below_pen(eventWindow, ev.x, ev.y); - - if (bpen_now != bpen_old) { - if (bpen_old) { - pen_send(bpen_old, Fl::Pen::LEAVE, State::NONE, event_data_copied); - } - below_pen_ = nullptr; - if (bpen_now) { - State state = (device_type_ == NSPointingDeviceTypeEraser) ? State::ERASER_HOVERS : State::TIP_HOVERS; - if (pen_send(bpen_now, Fl::Pen::ENTER, state, event_data_copied)) { - below_pen_ = subscriber_list_[bpen_now]; - Fl::belowmouse(bpen_now); - } - } - } - - receiver = below_pen_ ? below_pen_->widget() : nullptr; - if (!receiver) - return 0; - } - } else { - // Proximity events were handled earlier. - } - - if (!receiver) - return 0; - - if (is_down) { - if (!pushed) { - pushed_ = subscriber_list_[receiver]; - Fl::pushed(receiver); - } - State trigger = button_to_trigger([event buttonNumber], true); - if ([event buttonNumber] == 0) { - Fl::e_is_click = 1; - Fl::Private::e_x_down = (int)ev.x; - Fl::Private::e_y_down = (int)ev.y; - if ([event clickCount] > 1) - Fl::e_clicks++; - else - Fl::e_clicks = 0; - pen_send(receiver, Fl::Pen::TOUCH, trigger, event_data_copied); - } else { - pen_send(receiver, Fl::Pen::BUTTON_PUSH, trigger, event_data_copied); - } - } else if (is_up) { - if ( (ev.state & State::ANY_DOWN) == State::NONE ) { - Fl::pushed(nullptr); - pushed_ = nullptr; - } - State trigger = button_to_trigger([event buttonNumber], true); - if ([event buttonNumber] == 0) - pen_send(receiver, Fl::Pen::LIFT, trigger, event_data_copied); - else - pen_send(receiver, Fl::Pen::BUTTON_RELEASE, trigger, event_data_copied); - } else if (is_motion) { - if ( Fl::e_is_click && - ( (fabs((int)ev.x - Fl::Private::e_x_down) > 5) || - (fabs((int)ev.y - Fl::Private::e_y_down) > 5) ) ) - Fl::e_is_click = 0; - if (pushed) { - pen_send(receiver, Fl::Pen::DRAW, State::NONE, event_data_copied); - } else { - pen_send(receiver, Fl::Pen::HOVER, State::NONE, event_data_copied); - } - } - // Always return 1 because at this point, we capture pen events and don't - // want mouse events anymore! - return 1; -} diff --git a/src/drivers/Cocoa/Fl_Cocoa_Printer_Driver.mm b/src/drivers/Cocoa/Fl_Cocoa_Printer_Driver.mm deleted file mode 100644 index a40f1feb4..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Printer_Driver.mm +++ /dev/null @@ -1,455 +0,0 @@ -// -// Mac OS X-specific printing support (objective-c++) for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include -#include "../../Fl_Window_Driver.H" -#include "../../Fl_Screen_Driver.H" -#include "../Quartz/Fl_Quartz_Graphics_Driver.H" -#include "../Darwin/Fl_Darwin_System_Driver.H" -#include -#include "Fl_Cocoa_Window_Driver.H" - -#include -#include -#include -#include -#include -#import - -typedef OSStatus (*PMSessionSetDocumentFormatGeneration_type)( - PMPrintSession printSession, - CFStringRef docFormat, - CFArrayRef graphicsContextTypes, - CFTypeRef options); -typedef OSStatus (*PMSessionBeginDocumentNoDialog_type)( - PMPrintSession printSession, - PMPrintSettings printSettings, - PMPageFormat pageFormat); -typedef OSStatus -(*PMSessionGetGraphicsContext_type)( - PMPrintSession printSession, - CFStringRef graphicsContextType, - void ** graphicsContext); - - -/** Support for printing on the Apple OS X platform */ -class Fl_Cocoa_Printer_Driver : public Fl_Paged_Device { - friend class Fl_Printer; -protected: - float scale_x; - float scale_y; - float angle; // rotation angle in radians - PMPrintSession printSession; - PMPageFormat pageFormat; - PMPrintSettings printSettings; - Fl_Cocoa_Printer_Driver(void); - int begin_job(int pagecount = 0, int *frompage = NULL, int *topage = NULL, char **perr_message = NULL) FL_OVERRIDE; - int begin_page (void) FL_OVERRIDE; - int printable_rect(int *w, int *h) FL_OVERRIDE; - void margins(int *left, int *top, int *right, int *bottom) FL_OVERRIDE; - void origin(int *x, int *y) FL_OVERRIDE; - void origin(int x, int y) FL_OVERRIDE; - void scale (float scale_x, float scale_y = 0.) FL_OVERRIDE; - void rotate(float angle) FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate(void) FL_OVERRIDE; - int end_page (void) FL_OVERRIDE; - void end_job (void) FL_OVERRIDE; - ~Fl_Cocoa_Printer_Driver(void); -}; - - -Fl_Cocoa_Printer_Driver::Fl_Cocoa_Printer_Driver(void) -{ - x_offset = 0; - y_offset = 0; - scale_x = scale_y = 1.; - driver(new Fl_Quartz_Printer_Graphics_Driver); -} - -Fl_Paged_Device* Fl_Printer::newPrinterDriver(void) -{ - return new Fl_Cocoa_Printer_Driver(); -} - -Fl_Cocoa_Printer_Driver::~Fl_Cocoa_Printer_Driver(void) { - delete driver(); -} - -@interface print_panel_delegate : NSObject -- (void)printPanelDidEnd:(NSPrintPanel *)printPanel returnCode:(NSInteger)returnCode contextInfo:(NSInteger *)contextInfo; -@end -@implementation print_panel_delegate -- (void)printPanelDidEnd:(NSPrintPanel *)printPanel returnCode:(NSInteger)returnCode contextInfo:(NSInteger *)contextInfo -{ - *contextInfo = returnCode; -} -@end - -int Fl_Cocoa_Printer_Driver::begin_job (int pagecount, int *frompage, int *topage, char **perr_message) -//printing using a Quartz graphics context -//returns 0 iff OK -{ - OSStatus status = 0; - fl_open_display(); - Fl_Cocoa_Window_Driver::q_release_context(); - NSPrintInfo *info = [NSPrintInfo sharedPrintInfo]; - NSPrintPanel *panel = [NSPrintPanel printPanel]; - //from 10.5 - [panel setOptions:NSPrintPanelShowsCopies | NSPrintPanelShowsPageRange | - NSPrintPanelShowsPageSetupAccessory | NSPrintPanelShowsOrientation | NSPrintPanelShowsPaperSize]; - NSInteger retval = -1; - Fl_Window *top = Fl::first_window(); - NSWindow *main = (top ? (NSWindow*)fl_xid(top->top_window()) : nil); - if (main) { - [panel beginSheetWithPrintInfo:info - modalForWindow:main - delegate:[[[print_panel_delegate alloc] init] autorelease] - didEndSelector:@selector(printPanelDidEnd:returnCode:contextInfo:) - contextInfo:&retval]; - while (retval < 0) Fl::wait(100); - [main makeKeyAndOrderFront:nil]; - } else { - retval = [panel runModalWithPrintInfo:info]; //from 10.5 - } -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 - const NSInteger NSModalResponseOK = NSOKButton; -#endif - if (retval != NSModalResponseOK) return 1; - printSession = (PMPrintSession)[info PMPrintSession]; //from 10.5 - pageFormat = (PMPageFormat)[info PMPageFormat]; //from 10.5 - printSettings = (PMPrintSettings)[info PMPrintSettings];//from 10.5 - UInt32 from32, to32; - PMGetFirstPage(printSettings, &from32); - if (frompage) *frompage = (int)from32; - PMGetLastPage(printSettings, &to32); - if (topage) { - *topage = (int)to32; - if (*topage > pagecount && pagecount > 0) *topage = pagecount; - } - status = PMSessionBeginCGDocumentNoDialog(printSession, printSettings, pageFormat);//from 10.4 - - if (status != noErr) { - if (perr_message) { - NSError *nserr = [NSError errorWithDomain:NSCocoaErrorDomain code:status userInfo:nil]; - NSString *s = [nserr localizedDescription]; - if (s) *perr_message = fl_strdup([s UTF8String]); - } - return 2; - } - y_offset = x_offset = 0; - return 0; -} - -void Fl_Cocoa_Printer_Driver::margins(int *left, int *top, int *right, int *bottom) -{ - PMPaper paper; - PMGetPageFormatPaper(pageFormat, &paper); - PMOrientation orientation; - PMGetOrientation(pageFormat, &orientation); - PMPaperMargins margins; - PMPaperGetMargins(paper, &margins); - if(orientation == kPMPortrait) { - if (left) *left = (int)(margins.left / scale_x + 0.5); - if (top) *top = (int)(margins.top / scale_y + 0.5); - if (right) *right = (int)(margins.right / scale_x + 0.5); - if (bottom) *bottom = (int)(margins.bottom / scale_y + 0.5); - } else { - if (left) *left = (int)(margins.top / scale_x + 0.5); - if (top) *top = (int)(margins.left / scale_y + 0.5); - if (right) *right = (int)(margins.bottom / scale_x + 0.5); - if (bottom) *bottom = (int)(margins.right / scale_y + 0.5); - } -} - -int Fl_Cocoa_Printer_Driver::printable_rect(int *w, int *h) -//returns 0 iff OK -{ - OSStatus status; - PMRect pmRect; - int x, y; - - status = PMGetAdjustedPageRect(pageFormat, &pmRect); - if (status != noErr) return 1; - - x = (int)pmRect.left; - y = (int)pmRect.top; - *w = int((int)(pmRect.right - x) / scale_x + 1); - *h = int((int)(pmRect.bottom - y) / scale_y + 1); - return 0; -} - -void Fl_Cocoa_Printer_Driver::origin(int x, int y) -{ - x_offset = x; - y_offset = y; - CGContextRef gc = (CGContextRef)driver()->gc(); - CGContextRestoreGState(gc); - CGContextRestoreGState(gc); - CGContextSaveGState(gc); - CGContextScaleCTM(gc, scale_x, scale_y); - CGContextTranslateCTM(gc, x, y); - CGContextRotateCTM(gc, angle); - CGContextSaveGState(gc); -} - -void Fl_Cocoa_Printer_Driver::scale (float s_x, float s_y) -{ - if (s_y == 0.) s_y = s_x; - scale_x = s_x; - scale_y = s_y; - CGContextRef gc = (CGContextRef)driver()->gc(); - CGContextRestoreGState(gc); - CGContextRestoreGState(gc); - CGContextSaveGState(gc); - CGContextScaleCTM(gc, scale_x, scale_y); - CGContextRotateCTM(gc, angle); - x_offset = y_offset = 0; - CGContextSaveGState(gc); -} - -void Fl_Cocoa_Printer_Driver::rotate (float rot_angle) -{ - angle = - rot_angle * M_PI / 180.; - CGContextRef gc = (CGContextRef)driver()->gc(); - CGContextRestoreGState(gc); - CGContextRestoreGState(gc); - CGContextSaveGState(gc); - CGContextScaleCTM(gc, scale_x, scale_y); - CGContextTranslateCTM(gc, x_offset, y_offset); - CGContextRotateCTM(gc, angle); - CGContextSaveGState(gc); -} - -void Fl_Cocoa_Printer_Driver::translate(int x, int y) -{ - CGContextRef gc = (CGContextRef)driver()->gc(); - CGContextSaveGState(gc); - CGContextTranslateCTM(gc, x, y ); - CGContextSaveGState(gc); -} - -void Fl_Cocoa_Printer_Driver::untranslate(void) -{ - CGContextRef gc = (CGContextRef)driver()->gc(); - CGContextRestoreGState(gc); - CGContextRestoreGState(gc); -} - -int Fl_Cocoa_Printer_Driver::begin_page (void) -{ - OSStatus status = PMSessionBeginPageNoDialog(printSession, pageFormat, NULL); - CGContextRef gc; - status = PMSessionGetCGGraphicsContext(printSession, &gc); // 10.4 - driver()->gc(gc); - Fl_Surface_Device::push_current(this); - PMRect pmRect; - float win_scale_x, win_scale_y; - - PMPaper paper; - PMGetPageFormatPaper(pageFormat, &paper); - PMPaperMargins margins; - PMPaperGetMargins(paper, &margins); - PMOrientation orientation; - PMGetOrientation(pageFormat, &orientation); - - status = PMGetAdjustedPageRect(pageFormat, &pmRect); - double h = pmRect.bottom - pmRect.top; - x_offset = 0; - y_offset = 0; - angle = 0; - scale_x = scale_y = 1; - win_scale_x = win_scale_y = 1; - if(orientation == kPMPortrait) - CGContextTranslateCTM(gc, margins.left, margins.bottom + h); - else - CGContextTranslateCTM(gc, margins.top, margins.right + h); - CGContextScaleCTM(gc, win_scale_x, - win_scale_y); - ((Fl_Quartz_Graphics_Driver*)driver())->quartz_restore_line_style(); - CGContextSetShouldAntialias(gc, false); - CGContextSaveGState(gc); - CGContextSaveGState(gc); - fl_line_style(FL_SOLID); - fl_window = (FLWindow*)1; // TODO: something better - fl_clip_region(0); - return status != noErr; -} - -int Fl_Cocoa_Printer_Driver::end_page (void) -{ - CGContextRef gc = (CGContextRef)driver()->gc(); - CGContextFlush(gc); - CGContextRestoreGState(gc); - CGContextRestoreGState(gc); - OSStatus status = PMSessionEndPageNoDialog(printSession); - gc = NULL; - Fl_Surface_Device::pop_current(); - return status != noErr; -} - -void Fl_Cocoa_Printer_Driver::end_job (void) -{ - OSStatus status; - - status = PMSessionError(printSession); - if (status != noErr) { - fl_alert ("PM Session error %d", (int)status); - } - PMSessionEndDocumentNoDialog(printSession); - Fl_Window *w = Fl::first_window(); - if (w) w->show(); -} - -void Fl_Cocoa_Printer_Driver::origin(int *x, int *y) -{ - Fl_Paged_Device::origin(x, y); -} - - -class Fl_PDF_Cocoa_File_Surface : public Fl_Cocoa_Printer_Driver -{ -public: - char *doc_fname; - Fl_PDF_Cocoa_File_Surface(); - ~Fl_PDF_Cocoa_File_Surface() { if (doc_fname) free(doc_fname); } - int begin_job(const char *defaultname, - char **perr_message = NULL); - int begin_job(int, int*, int *, char **) FL_OVERRIDE {return 1;} // don't use - int begin_document(const char* outname, - enum Fl_Paged_Device::Page_Format format, - enum Fl_Paged_Device::Page_Layout layout, - char **perr_message); -}; - - -Fl_PDF_Cocoa_File_Surface::Fl_PDF_Cocoa_File_Surface() { - driver(new Fl_Quartz_Graphics_Driver()); - doc_fname = NULL; -} - - -int Fl_PDF_Cocoa_File_Surface::begin_job(const char* defaultfilename, - char **perr_message) { - OSStatus status = 0; - if (fl_mac_os_version < 100900) return 1; - Fl_Window *top = Fl::first_window(); - NSWindow *main = (top ? (NSWindow*)fl_xid(top->top_window()) : nil); - if (!main) return 1; - Fl_Cocoa_Window_Driver::q_release_context(); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 && defined(__BLOCKS__) - NSPDFInfo *pdf_info = [[NSPDFInfo alloc] init]; // 10.9 - NSPDFPanel *pdf_panel = [NSPDFPanel panel]; // 10.9 - char buf[FL_PATH_MAX]; - strcpy(buf, defaultfilename); - fl_filename_setext(buf, sizeof(buf), NULL); - [pdf_panel setDefaultFileName:[NSString stringWithUTF8String:buf]]; - [pdf_panel setOptions: NSPrintPanelShowsOrientation | NSPrintPanelShowsPaperSize]; - NSInteger retval = -1; - __block NSInteger complete = -1; - [pdf_panel beginSheetWithPDFInfo:pdf_info - modalForWindow:main - completionHandler:^(NSInteger returnCode) { - // this block runs after OK or Cancel was triggered in file dialog - complete = returnCode; - } - ]; - while (complete == -1) Fl::wait(100); // loop until end of file dialog - retval = complete; - [main makeKeyAndOrderFront:nil]; - if (retval != NSModalResponseOK) return 1; - NSURL *url = [pdf_info URL]; - doc_fname = fl_strdup([url fileSystemRepresentation]); - NSPrintInfo *pr_info = [NSPrintInfo sharedPrintInfo]; - [pr_info takeSettingsFromPDFInfo:pdf_info]; - [pdf_info release]; - printSession = (PMPrintSession)[pr_info PMPrintSession]; - printSettings = (PMPrintSettings)[pr_info PMPrintSettings]; - pageFormat = (PMPageFormat)[pr_info PMPageFormat]; - status = PMSessionBeginCGDocumentNoDialog(printSession, printSettings, pageFormat);//from 10.4 -#endif - if (status != noErr) { - if (perr_message) { - NSError *nserr = [NSError errorWithDomain:NSCocoaErrorDomain code:status userInfo:nil]; - NSString *s = [nserr localizedDescription]; - if (s) *perr_message = fl_strdup([s UTF8String]); - } - free(doc_fname); - doc_fname = NULL; - return 2; - } - y_offset = x_offset = 0; - return 0; -} - - -int Fl_PDF_Cocoa_File_Surface::begin_document(const char* outfname, - enum Fl_Paged_Device::Page_Format format, - enum Fl_Paged_Device::Page_Layout layout, - char **perr_message) { - OSStatus status = 0; - fl_open_display(); - if (fl_mac_os_version < 100900) return 1; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 - NSPDFInfo *pdf_info = [[NSPDFInfo alloc] init]; // 10.9 - doc_fname = fl_strdup(outfname); - NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:doc_fname]]; - [pdf_info setURL:url]; - NSSize psize = {(CGFloat)Fl_Paged_Device::page_formats[format].width, (CGFloat)Fl_Paged_Device::page_formats[format].height}; - [pdf_info setPaperSize:psize]; - [pdf_info setOrientation:(layout == PORTRAIT ? NSPaperOrientationPortrait : NSPaperOrientationLandscape)]; - NSPrintInfo *pr_info = [NSPrintInfo sharedPrintInfo]; - [pr_info takeSettingsFromPDFInfo:pdf_info]; - [pdf_info release]; - printSession = (PMPrintSession)[pr_info PMPrintSession]; - printSettings = (PMPrintSettings)[pr_info PMPrintSettings]; - pageFormat = (PMPageFormat)[pr_info PMPageFormat]; - status = PMSessionBeginCGDocumentNoDialog(printSession, printSettings, pageFormat);//from 10.4 -#endif - if (status != noErr) { - if (perr_message) { - NSError *nserr = [NSError errorWithDomain:NSCocoaErrorDomain code:status userInfo:nil]; - NSString *s = [nserr localizedDescription]; - if (s) *perr_message = fl_strdup([s UTF8String]); - } - free(doc_fname); - doc_fname = NULL; - return 2; - } - y_offset = x_offset = 0; - return 0; -} - - -Fl_Paged_Device *Fl_PDF_File_Surface::new_platform_pdf_surface_(const char ***pfname) { - Fl_PDF_Cocoa_File_Surface *surf = new Fl_PDF_Cocoa_File_Surface(); - *pfname = (const char**)&surf->doc_fname; - return surf; -} - - -int Fl_PDF_File_Surface::begin_job(const char* defaultfilename, - char **perr_message) { - return ((Fl_PDF_Cocoa_File_Surface*)platform_surface_)->begin_job(defaultfilename, perr_message); -} - - -int Fl_PDF_File_Surface::begin_document(const char* defaultfilename, - enum Fl_Paged_Device::Page_Format format, - enum Fl_Paged_Device::Page_Layout layout, - char **perr_message) { - return ((Fl_PDF_Cocoa_File_Surface*)platform_surface_)->begin_document(defaultfilename, format, layout, perr_message); -} diff --git a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H deleted file mode 100644 index 9e3d2386a..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H +++ /dev/null @@ -1,114 +0,0 @@ -// -// Definition of Apple Cocoa Screen interface -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Cocoa_Screen_Driver.H - \brief Definition of Apple Cocoa Screen interface. - */ - -#ifndef FL_COCOA_SCREEN_DRIVER_H -#define FL_COCOA_SCREEN_DRIVER_H - -#include "../../Fl_Screen_Driver.H" - -/* - Move everything here that manages the native screen interface. - - There is exactly one screen driver in the system. - - - screen configuration and sizes - - multiple screens - - native dialog boxes -*/ - - -class Fl_Window; -class Fl_Input; -class Fl_RGB_Image; -#ifdef __OBJC__ -@class NSImage; -#else -class NSImage; -#endif - -class Fl_Cocoa_Screen_Driver : public Fl_Screen_Driver -{ -protected: - struct XRectangle {int x, y, width, height;}; - XRectangle screens[MAX_SCREENS]; - float dpi_h[MAX_SCREENS]; - float dpi_v[MAX_SCREENS]; - static int insertion_point_x; - static int insertion_point_y; - static int insertion_point_height; - static bool insertion_point_location_is_valid; -public: - NSImage *default_icon; - Fl_Cocoa_Screen_Driver(); - static int next_marked_length; // next length of marked text after current marked text will have been replaced - static void breakMacEventLoop(); - // --- display management - // --- screen configuration - void init() FL_OVERRIDE; - int x() FL_OVERRIDE; - int y() FL_OVERRIDE; - int w() FL_OVERRIDE; - int h() FL_OVERRIDE; - void screen_xywh(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - void screen_dpi(float &h, float &v, int n=0) FL_OVERRIDE; - // implemented in Fl_cocoa.mm because uses Objective-c - void screen_work_area(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - // --- audible output - void beep(int type) FL_OVERRIDE; - // --- global events - void grab(Fl_Window* win) FL_OVERRIDE; - // --- global colors - void get_system_colors() FL_OVERRIDE; - int has_marked_text() const FL_OVERRIDE; - static void reset_marked_text(); - static void insertion_point_location(int x, int y, int height); - static int insertion_point_location(int *px, int *py, int *pheight); - int dnd(int use_selection) FL_OVERRIDE; - int compose(int &del) FL_OVERRIDE; - int input_widget_handle_key(int key, unsigned mods, unsigned shift, Fl_Input *input) FL_OVERRIDE; - int get_mouse(int &x, int &y) FL_OVERRIDE; - void enable_im() FL_OVERRIDE; - void disable_im() FL_OVERRIDE; - void open_display_platform() FL_OVERRIDE; - // --- compute dimensions of an Fl_Offscreen - void offscreen_size(Fl_Offscreen o, int &width, int &height) FL_OVERRIDE; - - APP_SCALING_CAPABILITY rescalable() FL_OVERRIDE { return SYSTEMWIDE_APP_SCALING; } - float scale(int /*nscreen*/) FL_OVERRIDE {return scale_;} - void scale(int /*nscreen*/, float f) FL_OVERRIDE { scale_ = f;} - Fl_RGB_Image *read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win, bool may_capture_subwins, bool *did_capture_subwins) FL_OVERRIDE; - void default_icons(const Fl_RGB_Image *icons[], int count) FL_OVERRIDE; - void copy(const char *stuff, int len, int clipboard, const char *type) FL_OVERRIDE; - void paste(Fl_Widget &receiver, int clipboard, const char *type) FL_OVERRIDE; - int clipboard_contains(const char *type) FL_OVERRIDE; - void set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) FL_OVERRIDE; - void reset_spot() FL_OVERRIDE; - int need_menu_handle_part2() FL_OVERRIDE {return 1;} - // these 2 are in Fl_get_key_mac.cxx - int event_key(int) FL_OVERRIDE; - int get_key(int) FL_OVERRIDE; -private: - float scale_; -}; - - -#endif // FL_COCOA_SCREEN_DRIVER_H diff --git a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx deleted file mode 100644 index 7eb98b291..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx +++ /dev/null @@ -1,434 +0,0 @@ -// -// Definition of Apple Cocoa Screen interface. -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include -#include "Fl_Cocoa_Screen_Driver.H" -#include "Fl_Cocoa_Window_Driver.H" -#include "../Quartz/Fl_Font.H" -#include -#include -#include -#include -#include -#include -#include - - -extern "C" void NSBeep(void); -extern void (*fl_lock_function)(); -extern void (*fl_unlock_function)(); - -int Fl_Cocoa_Screen_Driver::next_marked_length = 0; - - -// This key table is used for the Darwin system driver. It is defined here -// "static" and assigned in the constructor to avoid static initialization -// race conditions. It is used in fl_shortcut.cxx. -// -// This table must be in numeric order by fltk (X) keysym number: - -Fl_Screen_Driver::Keyname darwin_key_table[] = { - // v - this column may contain UTF-8 characters - {' ', "Space"}, - {FL_BackSpace, "⌫"/*"\xe2\x8c\xab"*/}, // U+232B : erase to the left - {FL_Tab, "⇥"/*"\xe2\x87\xa5"*/}, // U+21E5 rightwards arrow to bar - {FL_Enter, "↩"/*"\xe2\x86\xa9"*/}, // U+21A9 leftwards arrow with hook - {FL_Pause, "Pause"}, - {FL_Scroll_Lock, "Scroll_Lock"}, - {FL_Escape, "⎋"/*"\xe2\x8e\x8b"*/}, // U+238B : broken circle with northwest arrow - {FL_Home, "↖"/*"\xe2\x86\x96"*/}, // U+2196 north west arrow - {FL_Left, "←"/*"\xe2\x86\x90"*/}, // U+2190 leftwards arrow - {FL_Up, "↑"/*"\xe2\x86\x91"*/}, // U+2191 upwards arrow - {FL_Right, "→"/*"\xe2\x86\x92"*/}, // U+2192 rightwards arrow - {FL_Down, "↓"/*"\xe2\x86\x93"*/}, // U+2193 downwards arrow - {FL_Page_Up, "⇞"/*"\xe2\x87\x9e"*/}, // U+21DE upwards arrow with double stroke - {FL_Page_Down, "⇟"/*"\xe2\x87\x9f"*/}, // U+21DF downwards arrow with double stroke - {FL_End, "↘"/*"\xe2\x86\x98"*/}, // U+2198 south east arrow - {FL_Print, "Print"}, - {FL_Insert, "Insert"}, - {FL_Menu, "Menu"}, - {FL_Num_Lock, "Num_Lock"}, - {FL_KP_Enter, "⌤"/*"\xe2\x8c\xa4"*/}, // U+2324 up arrow head between two horizontal bars - {FL_Shift_L, "Shift_L"}, - {FL_Shift_R, "Shift_R"}, - {FL_Control_L, "Control_L"}, - {FL_Control_R, "Control_R"}, - {FL_Caps_Lock, "⇪"/*"\xe2\x87\xaa"*/}, // U+21EA upwards white arrow from bar - {FL_Meta_L, "Meta_L"}, - {FL_Meta_R, "Meta_R"}, - {FL_Alt_L, "Alt_L"}, - {FL_Alt_R, "Alt_R"}, - {FL_Delete, "⌦"/*"\xe2\x8c\xa6"*/} // U+2326 : erase to the right -}; - - -static Fl_Text_Editor::Key_Binding extra_bindings[] = { - // Define CMD+key accelerators... - { 'z', FL_COMMAND, Fl_Text_Editor::kf_undo ,0}, - { 'z', FL_COMMAND|FL_SHIFT, Fl_Text_Editor::kf_redo ,0}, - { 'x', FL_COMMAND, Fl_Text_Editor::kf_cut ,0}, - { 'c', FL_COMMAND, Fl_Text_Editor::kf_copy ,0}, - { 'v', FL_COMMAND, Fl_Text_Editor::kf_paste ,0}, - { 'a', FL_COMMAND, Fl_Text_Editor::kf_select_all ,0}, - { FL_Left, FL_COMMAND, Fl_Text_Editor::kf_meta_move ,0}, - { FL_Right, FL_COMMAND, Fl_Text_Editor::kf_meta_move ,0}, - { FL_Up, FL_COMMAND, Fl_Text_Editor::kf_meta_move ,0}, - { FL_Down, FL_COMMAND, Fl_Text_Editor::kf_meta_move ,0}, - { FL_Left, FL_COMMAND|FL_SHIFT, Fl_Text_Editor::kf_m_s_move ,0}, - { FL_Right, FL_COMMAND|FL_SHIFT, Fl_Text_Editor::kf_m_s_move ,0}, - { FL_Up, FL_COMMAND|FL_SHIFT, Fl_Text_Editor::kf_m_s_move ,0}, - { FL_Down, FL_COMMAND|FL_SHIFT, Fl_Text_Editor::kf_m_s_move ,0}, - { 0, 0, 0 ,0} -}; - - -Fl_Cocoa_Screen_Driver::Fl_Cocoa_Screen_Driver() { - text_editor_extra_key_bindings = extra_bindings; - scale_ = 1.; - default_icon = nil; - // initialize key table - key_table = darwin_key_table; - key_table_size = sizeof(darwin_key_table)/sizeof(*darwin_key_table); -} - - -void Fl_Cocoa_Screen_Driver::init() -{ - open_display(); - CGDirectDisplayID displays[MAX_SCREENS]; - CGDisplayCount count, i; - CGRect r; - CGGetActiveDisplayList(MAX_SCREENS, displays, &count); - for( i = 0; i < count; i++) { - r = CGDisplayBounds(displays[i]); - screens[i].x = int(r.origin.x); - screens[i].y = int(r.origin.y); - screens[i].width = int(r.size.width); - screens[i].height = int(r.size.height); - //fprintf(stderr,"screen %d %dx%dx%dx%d\n",i,screens[i].x,screens[i].y,screens[i].width,screens[i].height); - if (&CGDisplayScreenSize != NULL) { - CGSize s = CGDisplayScreenSize(displays[i]); // from 10.3 - dpi_h[i] = screens[i].width / (s.width/25.4); - dpi_v[i] = screens[i].height / (s.height/25.4); - } else { - dpi_h[i] = dpi_v[i] = 75.; - } - } - num_screens = count; -} - - -void Fl_Cocoa_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - - if ((n < 0) || (n >= num_screens)) - n = 0; - - float s = scale(0); - X = screens[n].x/s; - Y = screens[n].y/s; - W = screens[n].width/s; - H = screens[n].height/s; -} - - -void Fl_Cocoa_Screen_Driver::screen_dpi(float &h, float &v, int n) -{ - if (num_screens < 0) init(); - h = v = 0.0f; - - if (n >= 0 && n < num_screens) { - h = dpi_h[n]; - v = dpi_v[n]; - } -} - - -// Implements fl_beep(). See documentation in src/fl_ask.cxx. -void Fl_Cocoa_Screen_Driver::beep(int type) { - switch (type) { - case FL_BEEP_DEFAULT : - case FL_BEEP_ERROR : - NSBeep(); - break; - default : - break; - } -} - - -extern void fl_fix_focus(); // in Fl.cxx - -extern void *fl_capture; - - -void Fl_Cocoa_Screen_Driver::grab(Fl_Window* win) -{ - if (win) { - if (!Fl::grab_) { - fl_capture = (FLWindow*)(Fl_X::flx(Fl::first_window())->xid); - Fl_Cocoa_Window_Driver::driver(Fl::first_window())->set_key_window(); - } - Fl::grab_ = win; - } else { - if (Fl::grab_) { - fl_capture = 0; - Fl::grab_ = 0; - fl_fix_focus(); - } - } -} - - -static void set_selection_color(uchar r, uchar g, uchar b) -{ - Fl::set_color(FL_SELECTION_COLOR,r,g,b); -} - - -// MacOS X currently supports two color schemes - Blue and Graphite. -// Since we aren't emulating the Aqua interface (even if Apple would -// let us), we use some defaults that are similar to both. The -// Fl::scheme("plastic") color/box scheme provides a usable Aqua-like -// look-n-feel... -void Fl_Cocoa_Screen_Driver::get_system_colors() -{ - open_display(); - - Fl_Screen_Driver::get_system_colors(); - - if (!bg2_set) Fl::background2(0xff, 0xff, 0xff); - if (!fg_set) Fl::foreground(0, 0, 0); - if (!bg_set) Fl::background(0xd8, 0xd8, 0xd8); - -#if 0 - // this would be the correct code, but it does not run on all versions - // of OS X. Also, setting a bright selection color would require - // some updates in Fl_Adjuster and Fl_Help_Browser - OSStatus err; - RGBColor c; - err = GetThemeBrushAsColor(kThemeBrushPrimaryHighlightColor, 24, true, &c); - if (err) - set_selection_color(0x00, 0x00, 0x80); - else - set_selection_color(c.red, c.green, c.blue); -#else - set_selection_color(0x00, 0x00, 0x80); -#endif -} - - -int Fl_Cocoa_Screen_Driver::has_marked_text() const { - return 1; -} - - -int Fl_Cocoa_Screen_Driver::insertion_point_x = 0; -int Fl_Cocoa_Screen_Driver::insertion_point_y = 0; -int Fl_Cocoa_Screen_Driver::insertion_point_height = 0; -bool Fl_Cocoa_Screen_Driver::insertion_point_location_is_valid = false; - -void Fl_Cocoa_Screen_Driver::reset_marked_text() { - Fl::compose_state = 0; - next_marked_length = 0; - insertion_point_location_is_valid = false; -} - -// computes window coordinates & height of insertion point -int Fl_Cocoa_Screen_Driver::insertion_point_location(int *px, int *py, int *pheight) -// return true if the current coordinates of the insertion point are available -{ - if ( ! insertion_point_location_is_valid ) return false; - *px = insertion_point_x; - *py = insertion_point_y; - *pheight = insertion_point_height; - return true; -} - -void Fl_Cocoa_Screen_Driver::insertion_point_location(int x, int y, int height) { - insertion_point_location_is_valid = true; - insertion_point_x = x; - insertion_point_y = y; - insertion_point_height = height; -} - -int Fl_Cocoa_Screen_Driver::compose(int &del) { - int condition; - int has_text_key = Fl::compose_state || Fl::e_keysym <= '~' || Fl::e_keysym == FL_Iso_Key || - Fl::e_keysym == FL_JIS_Underscore || Fl::e_keysym == FL_Yen || - (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last && Fl::e_keysym != FL_KP_Enter); - condition = Fl::e_state&(FL_META | FL_CTRL) || - (Fl::e_keysym >= FL_Shift_L && Fl::e_keysym <= FL_Alt_R) || // called from flagsChanged - !has_text_key ; - if (condition) { del = 0; return 0;} // this stuff is to be treated as a function key - del = Fl::compose_state; - Fl::compose_state = next_marked_length; - return 1; -} - - -int Fl_Cocoa_Screen_Driver::input_widget_handle_key(int key, unsigned mods, unsigned shift, Fl_Input *input) -{ - switch (key) { - case FL_Delete: { - if (mods==0) return input->kf_delete_char_right(); // Delete (OSX-HIG,TE,SA,WOX) - if (mods==FL_CTRL) return input->kf_delete_char_right(); // Ctrl-Delete (??? TE,!SA,!WOX) - if (mods==FL_ALT) return input->kf_delete_word_right(); // Alt-Delete (OSX-HIG,TE,SA) - return 0; // ignore other combos, pass to parent - } - - case FL_Left: - if (mods==0) return input->kf_move_char_left(); // Left (OSX-HIG) - if (mods==FL_ALT) return input->kf_move_word_left(); // Alt-Left (OSX-HIG) - if (mods==FL_META) return input->kf_move_sol(); // Meta-Left (OSX-HIG) - if (mods==FL_CTRL) return input->kf_move_sol(); // Ctrl-Left (TE/SA) - return 0; // ignore other combos, pass to parent - - case FL_Right: - if (mods==0) return input->kf_move_char_right(); // Right (OSX-HIG) - if (mods==FL_ALT) return input->kf_move_word_right(); // Alt-Right (OSX-HIG) - if (mods==FL_META) return input->kf_move_eol(); // Meta-Right (OSX-HIG) - if (mods==FL_CTRL) return input->kf_move_eol(); // Ctrl-Right (TE/SA) - return 0; // ignore other combos, pass to parent - - case FL_Up: - if (mods==0) return input->kf_lines_up(1); // Up (OSX-HIG) - if (mods==FL_CTRL) return input->kf_page_up(); // Ctrl-Up (TE !HIG) - if (mods==FL_ALT) return input->kf_move_up_and_sol(); // Alt-Up (OSX-HIG) - if (mods==FL_META) return input->kf_top(); // Meta-Up (OSX-HIG) - return 0; // ignore other combos, pass to parent - - case FL_Down: - if (mods==0) return input->kf_lines_down(1); // Dn (OSX-HIG) - if (mods==FL_CTRL) return input->kf_page_down(); // Ctrl-Dn (TE !HIG) - if (mods==FL_ALT) return input->kf_move_down_and_eol(); // Alt-Dn (OSX-HIG) - if (mods==FL_META) return input->kf_bottom(); // Meta-Dn (OSX-HIG) - return 0; // ignore other combos, pass to parent - - case FL_Page_Up: - // Fl_Input has no scroll control, so instead we move the cursor by one page - // OSX-HIG recommends Alt increase one semantic unit, Meta next higher.. - if (mods==0) return input->kf_page_up(); // PgUp (OSX-HIG) - if (mods==FL_ALT) return input->kf_page_up(); // Alt-PageUp (OSX-HIG) - if (mods==FL_META) return input->kf_top(); // Meta-PageUp (OSX-HIG,!TE) - return 0; // ignore other combos, pass to parent - - case FL_Page_Down: - // Fl_Input has no scroll control, so instead we move the cursor by one page - // OSX-HIG recommends Alt increase one semantic unit, Meta next higher.. - if (mods==0) return input->kf_page_down(); // PgDn (OSX-HIG) - if (mods==FL_ALT) return input->kf_page_down(); // Alt-PageDn (OSX-HIG) - if (mods==FL_META) return input->kf_bottom(); // Meta-PageDn (OSX-HIG,!TE) - return 0; // ignore other combos, pass to parent - - case FL_Home: - if (mods==0) return input->kf_top(); // Home (OSX-HIG) - if (mods==FL_ALT) return input->kf_top(); // Alt-Home (???) - return 0; // ignore other combos, pass to parent - - case FL_End: - if (mods==0) return input->kf_bottom(); // End (OSX-HIG) - if (mods==FL_ALT) return input->kf_bottom(); // Alt-End (???) - return 0; // ignore other combos, pass to parent - - case FL_BackSpace: - if (mods==0) return input->kf_delete_char_left(); // Backspace (OSX-HIG) - if (mods==FL_CTRL) return input->kf_delete_char_left(); // Ctrl-Backspace (TE/SA) - if (mods==FL_ALT) return input->kf_delete_word_left(); // Alt-Backspace (OSX-HIG) - if (mods==FL_META) return input->kf_delete_sol(); // Meta-Backspace (OSX-HIG,!TE) - return 0; // ignore other combos, pass to parent - } - return -1; -} - -void Fl_Cocoa_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height) -{ - width = (int)CGBitmapContextGetWidth((CGContextRef)off); - height = (int)CGBitmapContextGetHeight((CGContextRef)off); -} - -Fl_RGB_Image *Fl_Cocoa_Screen_Driver::read_win_rectangle(int X, int Y, int w, int h, Fl_Window *window, - bool may_capture_subwins, bool *did_capture_subwins) -{ - int bpp, bpr; - uchar *base, *p; - if (!window) { // read from offscreen buffer - float s = 1; - CGContextRef src = (CGContextRef)Fl_Surface_Device::surface()->driver()->gc(); // get bitmap context - base = (uchar *)CGBitmapContextGetData(src); // get data - if(!base) return NULL; - int sw = (int)CGBitmapContextGetWidth(src); - int sh = (int)CGBitmapContextGetHeight(src); - if( (sw - X < w) || (sh - Y < h) ) return NULL; - bpr = (int)CGBitmapContextGetBytesPerRow(src); - bpp = (int)CGBitmapContextGetBitsPerPixel(src)/8; - Fl_Image_Surface *imgs = (Fl_Image_Surface*)Fl_Surface_Device::surface(); - int fltk_w, fltk_h; - imgs->printable_rect(&fltk_w, &fltk_h); - s = sw / float(fltk_w); - X *= s; Y *= s; w *= s; h *= s; - if (X + w > sw) w = sw - X; - if (Y + h > sh) h = sh - Y; - // Copy the image from the off-screen buffer to the memory buffer. - int idx, idy; // Current X & Y in image - uchar *pdst, *psrc; - p = new uchar[w * h * 4]; - for (idy = Y, pdst = p; idy < h + Y; idy ++) { - for (idx = 0, psrc = base + idy * bpr + X * bpp; idx < w; idx ++, psrc += bpp, pdst += 4) { - pdst[0] = psrc[0]; // R - pdst[1] = psrc[1]; // G - pdst[2] = psrc[2]; // B - } - } - bpr = 0; - } else { // read from window - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - CGImageRef cgimg = d->CGImage_from_window_rect(X, Y, w, h, may_capture_subwins); - if (did_capture_subwins) *did_capture_subwins = may_capture_subwins; - if (!cgimg) { - return NULL; - } - w = (int)CGImageGetWidth(cgimg); - h = (int)CGImageGetHeight(cgimg); - Fl_Image_Surface *surf = new Fl_Image_Surface(w, h); - Fl_Surface_Device::push_current(surf); - ((Fl_Quartz_Graphics_Driver*)fl_graphics_driver)->draw_CGImage(cgimg, 0, 0, w, h, 0, 0, w, h); - CGContextRef gc = (CGContextRef)fl_graphics_driver->gc(); - w = (int)CGBitmapContextGetWidth(gc); - h = (int)CGBitmapContextGetHeight(gc); - bpr = (int)CGBitmapContextGetBytesPerRow(gc); - bpp = (int)CGBitmapContextGetBitsPerPixel(gc)/8; - base = (uchar*)CGBitmapContextGetData(gc); - p = new uchar[bpr * h]; - memcpy(p, base, bpr * h); - Fl_Surface_Device::pop_current(); - delete surf; - CFRelease(cgimg); - } - Fl_RGB_Image *rgb = new Fl_RGB_Image(p, w, h, 4, bpr); - rgb->alloc_array = 1; - return rgb; -} - -void Fl_Cocoa_Screen_Driver::set_spot(int /*font*/, int size, int X, int Y, int /*W*/, int /*H*/, Fl_Window* /*win*/) { - Fl_Cocoa_Screen_Driver::insertion_point_location(X, Y, size); -} - -void Fl_Cocoa_Screen_Driver::reset_spot() { - Fl_Cocoa_Screen_Driver::reset_marked_text(); -} diff --git a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H b/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H deleted file mode 100644 index 358523dbd..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H +++ /dev/null @@ -1,168 +0,0 @@ -// -// Definition of Apple Cocoa window driver -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Cocoa_Window_Driver.H - \brief Definition of Apple Cocoa window driver. - */ - -#ifndef FL_COCOA_WINDOW_DRIVER_H -#define FL_COCOA_WINDOW_DRIVER_H - -#include "../../Fl_Window_Driver.H" -#include -#include - -class Fl_Image; -class Fl_Window; -#ifdef __OBJC__ -@class CALayer; -@class NSCursor; -@class NSImage; -@class FLWindow; -@class NSOpenGLContext; -@class NSOpenGLPixelFormat; -@class NSView; -@class NSWindow; -#else -class CALayer; -class NSCursor; -class NSImage; -class FLWindow; -class NSOpenGLContext; -class NSOpenGLPixelFormat; -class NSView; -class NSWindow; -#endif // __OBJC__ - -/** - \cond DriverDev - \addtogroup DriverDeveloper - \{ - */ - -/* - Move everything here that manages the native window interface. - - There is one window driver for each Fl_Window. Window drivers manage window - actions such as resizing, events, decoration, fullscreen modes, etc. . All - drawing and rendering is managed by the Surface device and the associated - graphics driver. - - - window specific event handling - - window types and styles, depth, etc. - - decorations - - ? where do we handle the interface between OpenGL/DirectX and Cocoa/Windows/Glx? - */ - -/** - \} - \endcond - */ - - -class Fl_Cocoa_Window_Driver : public Fl_Window_Driver -{ -private: - struct shape_data_type { - Fl_Image* shape_; ///< shape image - CGImageRef mask; - } *shape_data_; - void shape_bitmap_(Fl_Image* b); - void shape_alpha_(Fl_Image* img, int offset) FL_OVERRIDE; - CGRect* subRect_; // makes sure subwindow remains inside its parent window - // stores 3 binary flags: whether window is mapped to retina display; whether resolution just changed; - // whether window's view received the [FLView view_did_resize] message - unsigned window_flags_; -public: - Fl_Cocoa_Window_Driver(Fl_Window*); - ~Fl_Cocoa_Window_Driver(); - static inline Fl_Cocoa_Window_Driver* driver(const Fl_Window *w) {return (Fl_Cocoa_Window_Driver*)Fl_Window_Driver::driver(w);} - CGContextRef gc; // graphics context - NSCursor *cursor; - static void q_release_context(Fl_Cocoa_Window_Driver *x = 0); // free all resources associated with gc - static CGImageRef capture_decorated_window_10_5(NSWindow *nswin); - void set_key_window(); - bool mapped_to_retina(); // is window mapped to retina display? - void mapped_to_retina(bool); // sets whether window is mapped to retina display - bool changed_resolution(); // did window just moved to display with another resolution? - void changed_resolution(bool);// sets whether window just moved to display with another resolution - bool view_resized(); // did window's view receive [FLView view_did_resize] message? - void view_resized(bool b); // sets whether window's view received [FLView view_did_resize] message - bool through_resize(); // did Fl_Window::resize() run already - void through_resize(bool b); // set whether Fl_Window::resize() run already - CGRect* subRect() { return subRect_; } // getter - void subRect(CGRect *r) { subRect_ = r; } // setter - static void destroy(FLWindow*); - CGImageRef CGImage_from_window_rect(int x, int y, int w, int h, bool capture_subwins = true); - - // --- window data - int decorated_w() FL_OVERRIDE; - int decorated_h() FL_OVERRIDE; - const Fl_Image* shape() FL_OVERRIDE; - - // --- window management - void makeWindow() FL_OVERRIDE; - void take_focus() FL_OVERRIDE; - void flush() FL_OVERRIDE; - void flush_overlay() FL_OVERRIDE; - void draw_begin() FL_OVERRIDE; - void draw_end() FL_OVERRIDE; - void make_current() FL_OVERRIDE; - void label(const char *name, const char *mininame) FL_OVERRIDE; - void show() FL_OVERRIDE; - void resize(int X,int Y,int W,int H) FL_OVERRIDE; - void hide() FL_OVERRIDE; - void map() FL_OVERRIDE; - void unmap() FL_OVERRIDE; - void fullscreen_on() FL_OVERRIDE; - void fullscreen_off(int X, int Y, int W, int H) FL_OVERRIDE; - void fullscreen_screens(bool on_off) FL_OVERRIDE; - void maximize() FL_OVERRIDE; - void un_maximize() FL_OVERRIDE; - void use_border() FL_OVERRIDE; - void size_range() FL_OVERRIDE; - void iconize() FL_OVERRIDE; - void decoration_sizes(int *top, int *left, int *right, int *bottom) FL_OVERRIDE; - // --- window cursor stuff - int set_cursor(Fl_Cursor) FL_OVERRIDE; - int set_cursor(const Fl_RGB_Image*, int, int) FL_OVERRIDE; - - void shape(const Fl_Image* img) FL_OVERRIDE; - // next 4 are in Fl_cocoa.mm because they use Objective-c - void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) FL_OVERRIDE; - void wait_for_expose() FL_OVERRIDE; - void draw_titlebar_to_context(CGContextRef gc, int w, int h); - int scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, void (*draw_area)(void*, int,int,int,int), void* data) FL_OVERRIDE; - - //icons - void icons(const Fl_RGB_Image *icons[], int count) FL_OVERRIDE; - NSImage *icon_image; - - fl_uintptr_t os_id() FL_OVERRIDE; -}; - -class Fl_Cocoa_Plugin : public Fl_Plugin { -public: - Fl_Cocoa_Plugin(const char *pluginName) : Fl_Plugin(klass(), pluginName) { } - virtual const char *klass() { return "fltk:cocoa"; } - virtual const char *name() = 0; - virtual void resize(Fl_Gl_Window *glw, int x, int y, int w, int h) = 0; -}; - -#endif // FL_COCOA_WINDOW_DRIVER_H diff --git a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx b/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx deleted file mode 100644 index 55d1bea32..000000000 --- a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx +++ /dev/null @@ -1,294 +0,0 @@ -// -// Definition of Apple Cocoa window driver. -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include -#include "Fl_Cocoa_Window_Driver.H" -#include "../../Fl_Screen_Driver.H" -#include "../Quartz/Fl_Quartz_Graphics_Driver.H" -#include -#include -#include -#include -#include -#include -#include - - -Fl_Cocoa_Window_Driver::Fl_Cocoa_Window_Driver(Fl_Window *win) -: Fl_Window_Driver(win) -{ - cursor = nil; - window_flags_ = 0; - icon_image = NULL; - screen_num_ = 0; - shape_data_ = NULL; -} - - -void Fl_Cocoa_Window_Driver::take_focus() -{ - set_key_window(); -} - - -void Fl_Cocoa_Window_Driver::flush_overlay() -{ - Fl_Overlay_Window *oWindow = pWindow->as_overlay_window(); - int erase_overlay = (pWindow->damage()&FL_DAMAGE_OVERLAY) | (overlay() == oWindow); - pWindow->clear_damage((uchar)(pWindow->damage()&~FL_DAMAGE_OVERLAY)); - - if (!oWindow->shown()) return; - pWindow->make_current(); // make sure fl_gc is non-zero - if (!other_xid) { - other_xid = new Fl_Image_Surface(oWindow->w(), oWindow->h(), 1); - oWindow->clear_damage(FL_DAMAGE_ALL); - } - if (oWindow->damage() & ~FL_DAMAGE_EXPOSE) { - Fl_X *myi = Fl_X::flx(pWindow); - fl_clip_region(myi->region); myi->region = 0; - Fl_Surface_Device::push_current(other_xid); - draw(); - Fl_Surface_Device::pop_current(); - } - if (erase_overlay) fl_clip_region(0); - fl_copy_offscreen(0, 0, oWindow->w(), oWindow->h(), other_xid->offscreen(), 0, 0); - if (overlay() == oWindow) oWindow->draw_overlay(); -} - - -void Fl_Cocoa_Window_Driver::draw_begin() -{ - if (!Fl_Surface_Device::surface()->driver()->has_feature(Fl_Graphics_Driver::NATIVE)) return; - CGContextRef my_gc = (CGContextRef)Fl_Surface_Device::surface()->driver()->gc(); - if (shape_data_) { - if (shape_data_->mask) { - CGContextClipToMask(my_gc, CGRectMake(0,0,w(),h()), shape_data_->mask); // requires Mac OS 10.4 - } - CGContextSaveGState(my_gc); - } -} - - -void Fl_Cocoa_Window_Driver::draw_end() -{ - if (Fl_Surface_Device::surface()->driver()->has_feature(Fl_Graphics_Driver::NATIVE)) { - CGContextRef my_gc = (CGContextRef)Fl_Surface_Device::surface()->driver()->gc(); - if (shape_data_) CGContextRestoreGState(my_gc); - } -} - - - -static void MyProviderReleaseData (void *info, const void *data, size_t size) { - delete[] (uchar*)data; -} - -// bitwise inversion of all 4-bit quantities -static const unsigned char swapped[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}; - -static inline uchar swap_byte(const uchar b) { - // reverse the order of bits of byte b: 1->8 becomes 8->1 - return (swapped[b & 0xF] << 4) | swapped[b >> 4]; -} - - -void Fl_Cocoa_Window_Driver::shape_bitmap_(Fl_Image* b) { - shape_data_->shape_ = b; - if (b) { - // complement mask bits and perform bitwise inversion of all bytes and also reverse top and bottom - int bytes_per_row = (b->w() + 7)/8; - uchar *from = new uchar[bytes_per_row * b->h()]; - for (int i = 0; i < b->h(); i++) { - uchar *p = (uchar*)(*b->data()) + bytes_per_row * i; - uchar *last = p + bytes_per_row; - uchar *q = from + (b->h() - 1 - i) * bytes_per_row; - while (p < last) { - *q++ = swap_byte(~*p++); - } - } - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, bytes_per_row * b->h(), MyProviderReleaseData); - shape_data_->mask = CGImageMaskCreate(b->w(), b->h(), 1, 1, bytes_per_row, provider, NULL, false); - CFRelease(provider); - } -} - - -void Fl_Cocoa_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { - int i, d = img->d(), w = img->w(), h = img->h(); - shape_data_->shape_ = img; - if (shape_data_->shape_) { - // reverse top and bottom and convert to gray scale if img->d() == 3 and complement bits - int bytes_per_row = w * d; - uchar *from = new uchar[w * h]; - for ( i = 0; i < h; i++) { - uchar *p = (uchar*)(*img->data()) + bytes_per_row * i + offset; - uchar *last = p + bytes_per_row; - uchar *q = from + (h - 1 - i) * w; - while (p < last) { - if (d == 3) { - unsigned u = *p++; - u += *p++; - u += *p++; - *q++ = ~(u/3); - } - else { - *q++ = ~(*p); - p += d; - } - } - } - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, w * h, MyProviderReleaseData); - shape_data_->mask = CGImageMaskCreate(w, h, 8, 8, w, provider, NULL, false); - CFRelease(provider); - } -} - - -void Fl_Cocoa_Window_Driver::shape(const Fl_Image* img) { - if (shape_data_) { - if (shape_data_->mask) { CGImageRelease(shape_data_->mask); } - } - else { - shape_data_ = new shape_data_type; - } - memset(shape_data_, 0, sizeof(shape_data_type)); - int d = img->d(); - if (d && img->count() >= 2) { - shape_pixmap_((Fl_Image*)img); - shape_data_->shape_ = (Fl_Image*)img; - } - else if (d == 0) shape_bitmap_((Fl_Image*)img); - else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); - else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); - pWindow->border(false); -} - - -void Fl_Cocoa_Window_Driver::hide() { - Fl_X* ip = Fl_X::flx(pWindow); - // MacOS X manages a single pointer per application. Make sure that hiding - // a toplevel window will not leave us with some random pointer shape, or - // worst case, an invisible pointer - if (ip && !parent()) pWindow->cursor(FL_CURSOR_DEFAULT); - if ( hide_common() ) return; - q_release_context(this); - if ( ip->xid == (fl_uintptr_t)fl_window ) - fl_window = 0; - if (ip->region) Fl_Graphics_Driver::default_driver().XDestroyRegion(ip->region); - destroy((FLWindow*)ip->xid); - delete subRect(); - delete ip; -} - - -int Fl_Cocoa_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, void (*draw_area)(void*, int,int,int,int), void* data) -{ - if ( (src_x < 0) || (src_y < 0) ) - return 1; - if ( (src_x+src_w > pWindow->w()) || (src_y+src_h > pWindow->h()) ) - return 1; - CGImageRef img = CGImage_from_window_rect(src_x, src_y, src_w, src_h); - if (!img) - return 1; - // the current surface is generally the display, but is an Fl_Image_Surface when scrolling an Fl_Overlay_Window - Fl_Quartz_Graphics_Driver *qgd = (Fl_Quartz_Graphics_Driver*)Fl_Surface_Device::surface()->driver(); - float s = qgd->scale(); - qgd->draw_CGImage(img, dest_x, dest_y, (int)lround(s*src_w), (int)lround(s*src_h), 0, 0, src_w, src_h); - CFRelease(img); - return 0; -} - -static const unsigned mapped_mask = 1; -static const unsigned changed_mask = 2; -static const unsigned view_resized_mask = 4; -static const unsigned through_resize_mask = 8; - -bool Fl_Cocoa_Window_Driver::mapped_to_retina() { - return window_flags_ & mapped_mask; -} - -void Fl_Cocoa_Window_Driver::mapped_to_retina(bool b) { - if (b) window_flags_ |= mapped_mask; - else window_flags_ &= ~mapped_mask; -} - -bool Fl_Cocoa_Window_Driver::changed_resolution() { - return window_flags_ & changed_mask; -} - -void Fl_Cocoa_Window_Driver::changed_resolution(bool b) { - if (b) window_flags_ |= changed_mask; - else window_flags_ &= ~changed_mask; -} - -bool Fl_Cocoa_Window_Driver::view_resized() { - return window_flags_ & view_resized_mask; -} - -void Fl_Cocoa_Window_Driver::view_resized(bool b) { - if (b) window_flags_ |= view_resized_mask; - else window_flags_ &= ~view_resized_mask; -} - -bool Fl_Cocoa_Window_Driver::through_resize() { - return window_flags_ & through_resize_mask; -} - -void Fl_Cocoa_Window_Driver::through_resize(bool b) { - if (b) window_flags_ |= through_resize_mask; - else window_flags_ &= ~through_resize_mask; -} - -const Fl_Image* Fl_Cocoa_Window_Driver::shape() { - return shape_data_ ? shape_data_->shape_ : NULL; -} - -/* Returns images of the capture of the window title-bar. - On the Mac OS platform, left, bottom and right are returned NULL; top is returned with depth 4. - */ -void Fl_Cocoa_Window_Driver::capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) -{ - top = left = bottom = right = NULL; - int htop, hleft, hright, hbottom; - Fl_Cocoa_Window_Driver::decoration_sizes(&htop, &hleft, &hright, &hbottom); - if (htop == 0) return; // when window is fullscreen - CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); - float s = Fl::screen_driver()->scale(screen_num()); - int scaled_w = int(w() * s); - const int factor = (mapped_to_retina() ? 2 : 1); - int data_w = factor * scaled_w, data_h = factor * htop; - uchar *rgba = new uchar[4 * data_w * data_h]; - CGContextRef auxgc = CGBitmapContextCreate(rgba, data_w, data_h, 8, 4 * data_w, cspace, kCGImageAlphaPremultipliedLast); - CGColorSpaceRelease(cspace); - CGContextClearRect(auxgc, CGRectMake(0,0,data_w,data_h)); - CGContextScaleCTM(auxgc, factor, factor); - draw_titlebar_to_context(auxgc, scaled_w, htop); - top = new Fl_RGB_Image(rgba, data_w, data_h, 4); - top->alloc_array = 1; - top->scale(w(),htop, s <1 ? 0 : 1, 1); - CGContextRelease(auxgc); -} - - -FLWindow *fl_mac_xid(const Fl_Window *win) { - return (FLWindow*)Fl_Window_Driver::xid(win); -} - - -Fl_Window *fl_mac_find(FLWindow *xid) { - return Fl_Window_Driver::find((fl_uintptr_t)xid); -} diff --git a/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H b/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H deleted file mode 100644 index 2de8bde64..000000000 --- a/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H +++ /dev/null @@ -1,51 +0,0 @@ -// -// Definition of class Fl_MacOS_Sys_Menu_Bar_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2017 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef Fl_MacOS_Sys_Menu_Bar_Driver_H -#define Fl_MacOS_Sys_Menu_Bar_Driver_H - -#include "../../Fl_Sys_Menu_Bar_Driver.H" - -class Fl_MacOS_Sys_Menu_Bar_Driver : public Fl_Sys_Menu_Bar_Driver { -public: - Fl_Menu_Item *window_menu_items; - int first_window_menu_item; - Fl_MacOS_Sys_Menu_Bar_Driver(); - virtual ~Fl_MacOS_Sys_Menu_Bar_Driver(); - void update() FL_OVERRIDE; - void draw() FL_OVERRIDE; - void about(Fl_Callback *cb, void *data) FL_OVERRIDE; - int add(const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) FL_OVERRIDE; - int add(const char* str) FL_OVERRIDE; - int insert(int index, const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) FL_OVERRIDE; - void menu(const Fl_Menu_Item *m) FL_OVERRIDE; - void shortcut (int i, int s) FL_OVERRIDE; - void setonly (Fl_Menu_Item *item) FL_OVERRIDE; - void clear() FL_OVERRIDE; - int clear_submenu(int index) FL_OVERRIDE; - void remove(int index) FL_OVERRIDE; - void replace(int index, const char *name) FL_OVERRIDE; - void mode(int i, int fl) FL_OVERRIDE; - void create_window_menu() FL_OVERRIDE; - void new_window(Fl_Window *win); - void remove_window(Fl_Window *win); - void rename_window(Fl_Window *win); - static Fl_MacOS_Sys_Menu_Bar_Driver* driver(); - void play_menu(const Fl_Menu_Item *) FL_OVERRIDE; -}; - - -#endif /* Fl_MacOS_Sys_Menu_Bar_Driver_H */ diff --git a/src/drivers/Darwin/Fl_Darwin_System_Driver.H b/src/drivers/Darwin/Fl_Darwin_System_Driver.H deleted file mode 100644 index 7dd0ae6da..000000000 --- a/src/drivers/Darwin/Fl_Darwin_System_Driver.H +++ /dev/null @@ -1,85 +0,0 @@ -// -// Definition of Apple Darwin system driver -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2021 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Darwin_System_Driver.H - \brief Definition of Apple Darwin system driver. - */ - -#ifndef FL_DARWIN_SYSTEM_DRIVER_H -#define FL_DARWIN_SYSTEM_DRIVER_H - -#include "../Posix/Fl_Posix_System_Driver.H" -#include -#include - -/* - Move everything here that manages the system interface. - - There is exactly one system driver. - - - filename and pathname management - - directory and file access - - system time and system timer - - multithreading - */ - -class Fl_Darwin_System_Driver : public Fl_Posix_System_Driver -{ -public: - Fl_Darwin_System_Driver(); - int single_arg(const char *arg) FL_OVERRIDE; - int arg_and_value(const char *name, const char *value) FL_OVERRIDE; - int clocale_vprintf(FILE *output, const char *format, va_list args) FL_OVERRIDE; - int clocale_vsnprintf(char *output, size_t output_size, const char *format, va_list args) FL_OVERRIDE; - int clocale_vsscanf(const char *input, const char *format, va_list args) FL_OVERRIDE; - static void *get_carbon_function(const char *name); - static int calc_mac_os_version(); // computes the fl_mac_os_version global variable - static unsigned short *compute_macKeyLookUp(); - - int filename_list(const char *d, dirent ***list, - int (*sort)(struct dirent **, struct dirent **), - char *errmsg=NULL, int errmsg_sz=0) FL_OVERRIDE; - int open_uri(const char *uri, char *msg, int msglen) FL_OVERRIDE; - int need_test_shortcut_extra() FL_OVERRIDE {return 1;} - int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon) FL_OVERRIDE; - void newUUID(char *uuidBuffer) FL_OVERRIDE; - char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor, - const char *application) FL_OVERRIDE; - const char *local_to_latin1(const char *t, int n) FL_OVERRIDE; - const char *latin1_to_local(const char *t, int n) FL_OVERRIDE; - const char *local_to_mac_roman(const char *t, int n) FL_OVERRIDE; - const char *mac_roman_to_local(const char *t, int n) FL_OVERRIDE; - void tree_draw_expando_button(int x, int y, bool state, bool active) FL_OVERRIDE; - int tree_connector_style() FL_OVERRIDE; - const char *filename_name(const char *buf) FL_OVERRIDE; - void add_fd(int fd, int when, Fl_FD_Handler cb, void* = 0) FL_OVERRIDE; - void add_fd(int fd, Fl_FD_Handler cb, void* = 0) FL_OVERRIDE; - void remove_fd(int, int when) FL_OVERRIDE; - void remove_fd(int) FL_OVERRIDE; - void open_callback(void (*)(const char *)) FL_OVERRIDE; - const char *shift_name() FL_OVERRIDE; - const char *meta_name() FL_OVERRIDE; - const char *alt_name() FL_OVERRIDE; - const char *control_name() FL_OVERRIDE; - Fl_Sys_Menu_Bar_Driver *sys_menu_bar_driver() FL_OVERRIDE; - double wait(double time_to_wait) FL_OVERRIDE; - int ready() FL_OVERRIDE; - int filename_relative(char *to, int tolen, const char *dest_dir, const char *base_dir) FL_OVERRIDE; -}; - -#endif // FL_DARWIN_SYSTEM_DRIVER_H diff --git a/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx b/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx deleted file mode 100644 index c11a95f94..000000000 --- a/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx +++ /dev/null @@ -1,339 +0,0 @@ -// -// Definition of Apple Darwin system driver. -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "Fl_Darwin_System_Driver.H" -#include "../Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H" -#include -#include -#include -#include -#include -#include -#include "../../flstring.h" -#include -#include // 10.4 -#include -#include -#include -#include -#include -#include -#include -#include - - -const char *Fl_Darwin_System_Driver::shift_name() { - return "⇧\\"; // "\xe2\x87\xa7\\"; // U+21E7 (upwards white arrow) -} -const char *Fl_Darwin_System_Driver::meta_name() { - return "⌘\\"; // "\xe2\x8c\x98\\"; // U+2318 (place of interest sign) -} -const char *Fl_Darwin_System_Driver::alt_name() { - return "⌥\\"; // "\xe2\x8c\xa5\\"; // U+2325 (option key) -} -const char *Fl_Darwin_System_Driver::control_name() { - return "⌃\\"; // "\xe2\x8c\x83\\"; // U+2303 (up arrowhead) -} - -Fl_Darwin_System_Driver::Fl_Darwin_System_Driver() : Fl_Posix_System_Driver() { - if (fl_mac_os_version == 0) fl_mac_os_version = calc_mac_os_version(); - command_key = FL_META; - control_key = FL_CTRL; -} - -int Fl_Darwin_System_Driver::single_arg(const char *arg) { - // The Finder application in MacOS X passes the "-psn_N_NNNNN" option to all apps. - return (strncmp(arg, "psn_", 4) == 0); -} - -int Fl_Darwin_System_Driver::arg_and_value(const char *name, const char *value) { - // Xcode in MacOS X may pass "-NSDocumentRevisionsDebugMode YES" - return strcmp(name, "NSDocumentRevisionsDebugMode") == 0; -} - -static locale_t postscript_locale = NULL; - -int Fl_Darwin_System_Driver::clocale_vprintf(FILE *output, const char *format, va_list args) { - if (!postscript_locale) - postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); - return vfprintf_l(output, postscript_locale, format, args); // 10.4 -} - -int Fl_Darwin_System_Driver::clocale_vsnprintf(char *output, size_t output_size, const char *format, va_list args) { - if (!postscript_locale) - postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); - return vsnprintf_l(output, output_size, postscript_locale, format, args); // 10.4 -} - -int Fl_Darwin_System_Driver::clocale_vsscanf(const char *input, const char *format, va_list args) { - if (!postscript_locale) - postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); - return vsscanf_l(input, postscript_locale, format, args); // 10.4 -} - - -// Returns the address of a Carbon function after dynamically loading the Carbon library if needed. -void *Fl_Darwin_System_Driver::get_carbon_function(const char *function_name) { - static void *carbon = ::dlopen("/System/Library/Frameworks/Carbon.framework/Carbon", RTLD_LAZY); - return (carbon ? dlsym(carbon, function_name) : NULL); -} - -int Fl_Darwin_System_Driver::filename_list(const char *d, dirent ***list, - int (*sort)(struct dirent **, struct dirent **), - char *errmsg, int errmsg_sz) { - int dirlen; - char *dirloc; - // Assume that locale encoding is no less dense than UTF-8 - dirlen = (int)strlen(d); - dirloc = (char *)d; -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - int n = scandir(dirloc, list, 0, (int(*)(const struct dirent**,const struct dirent**))sort); -# else - int n = scandir(dirloc, list, 0, (int(*)(const void*,const void*))sort); -# endif - if (n==-1) { - if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); - return -1; - } - // convert every filename to UTF-8, and append a '/' to all - // filenames that are directories - int i; - char *fullname = (char*)malloc(dirlen+FL_PATH_MAX+3); // Add enough extra for two /'s and a nul - // Use memcpy for speed since we already know the length of the string... - memcpy(fullname, d, dirlen+1); - char *name = fullname + dirlen; - if (name!=fullname && name[-1]!='/') *name++ = '/'; - for (i=0; id_name); - newlen = len; - dirent *newde = (dirent*)malloc(de->d_name - (char*)de + newlen + 2); // Add space for a / and a nul - // Conversion to UTF-8 - memcpy(newde, de, de->d_name - (char*)de); - strcpy(newde->d_name, de->d_name); - // Check if dir (checks done on "old" name as we need to interact with - // the underlying OS) - if (de->d_name[len-1]!='/' && len<=FL_PATH_MAX) { - // Use memcpy for speed since we already know the length of the string... - memcpy(name, de->d_name, len+1); - if (fl_filename_isdir(fullname)) { - char *dst = newde->d_name + newlen; - *dst++ = '/'; - *dst = 0; - } - } - free(de); - (*list)[i] = newde; - } - free(fullname); - return n; -} - - -int Fl_Darwin_System_Driver::open_uri(const char *uri, char *msg, int msglen) -{ - char *argv[3]; // Command-line arguments - argv[0] = (char*)"open"; - argv[1] = (char*)uri; - argv[2] = (char*)0; - if (msg) snprintf(msg, msglen, "open %s", uri); - return run_program("/usr/bin/open", argv, msg, msglen) != 0; -} - -int Fl_Darwin_System_Driver::file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon) -{ - // MacOS X and Darwin use getfsstat() system call... - int numfs; // Number of file systems - struct statfs *fs; // Buffer for file system info - int num_files = 0; - - // We always have the root filesystem. - browser->add("/", icon); - - // Get the mounted filesystems... - numfs = getfsstat(NULL, 0, MNT_NOWAIT); - if (numfs > 0) { - // We have file systems, get them... - fs = new struct statfs[numfs]; - getfsstat(fs, sizeof(struct statfs) * numfs, MNT_NOWAIT); - - // Add filesystems to the list... - for (int i = 0; i < numfs; i ++) { - // Ignore "/", "/dev", and "/.vol"... - if (fs[i].f_mntonname[1] && strcmp(fs[i].f_mntonname, "/dev") && - strcmp(fs[i].f_mntonname, "/.vol")) { - snprintf(filename, lname, "%s/", fs[i].f_mntonname); - browser->add(filename, icon); - } - num_files ++; - } - - // Free the memory used for the file system info array... - delete[] fs; - } - return num_files; -} - -void Fl_Darwin_System_Driver::newUUID(char *uuidBuffer) -{ - CFUUIDRef theUUID = CFUUIDCreate(NULL); - CFUUIDBytes b = CFUUIDGetUUIDBytes(theUUID); - snprintf(uuidBuffer, 36+1, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", - b.byte0, b.byte1, b.byte2, b.byte3, b.byte4, b.byte5, b.byte6, b.byte7, - b.byte8, b.byte9, b.byte10, b.byte11, b.byte12, b.byte13, b.byte14, b.byte15); - CFRelease(theUUID); -} - -/* - * returns pointer to the filename, or null if name ends with ':' - */ -const char *Fl_Darwin_System_Driver::filename_name( const char *name ) -{ - const char *p, *q; - if (!name) return (0); - for ( p = q = name ; *p ; ) { - if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) { - q = p+2; - p++; - } - else if (p[0] == '/') { - q = p + 1; - } - p++; - } - return q; -} - -// These function assume a western code page. If you need to support -// scripts that are not part of this code page, you might want to -// take a look at FLTK2, which uses utf8 for text encoding. -// -// By keeping these conversion tables in their own module, they will not -// be statically linked (by a smart linker) unless actually used. -// -// On MS-Windows, nothing need to be converted. We simply return the -// original pointer. -// -// Most X11 implementations seem to default to Latin-1 as a code since it -// is a superset of ISO 8859-1, the original Western codepage on X11. -// -// Apple's OS X however renders text in MacRoman for western settings. The -// lookup tables below will convert all common character codes and replace -// unknown characters with an upside-down question mark. - -// This table converts Windows-1252/Latin 1 into MacRoman encoding -static uchar latin2roman[128] = { -0xdb, 0xc0, 0xe2, 0xc4, 0xe3, 0xc9, 0xa0, 0xe0, 0xf6, 0xe4, 0xc0, 0xdc, 0xce, 0xc0, 0xc0, 0xc0, -0xc0, 0xd4, 0xd5, 0xd2, 0xd3, 0xa5, 0xd0, 0xd1, 0xf7, 0xaa, 0xc0, 0xdd, 0xcf, 0xc0, 0xc0, 0xd9, -0xca, 0xc1, 0xa2, 0xa3, 0xc0, 0xb4, 0xc0, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xc0, 0xa8, 0xf8, -0xa1, 0xb1, 0xc0, 0xc0, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xc0, 0xbc, 0xc8, 0xc0, 0xc0, 0xc0, 0xc0, -0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, -0xc0, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xc0, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xc0, 0xc0, 0xa7, -0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, -0xc0, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xc0, 0xc0, 0xd8 -}; - -// This table converts MacRoman into Windows-1252/Latin 1 -static uchar roman2latin[128] = { -0xc4, 0xc5, 0xc7, 0xc9, 0xd1, 0xd6, 0xdc, 0xe1, 0xe0, 0xe2, 0xe4, 0xe3, 0xe5, 0xe7, 0xe9, 0xe8, -0xea, 0xeb, 0xed, 0xec, 0xee, 0xef, 0xf1, 0xf3, 0xf2, 0xf4, 0xf6, 0xf5, 0xfa, 0xf9, 0xfb, 0xfc, -0x86, 0xb0, 0xa2, 0xa3, 0xa7, 0x95, 0xb6, 0xdf, 0xae, 0xa9, 0x99, 0xb4, 0xa8, 0xbf, 0xc6, 0xd8, -0xbf, 0xb1, 0xbf, 0xbf, 0xa5, 0xb5, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xaa, 0xba, 0xbf, 0xe6, 0xf8, -0xbf, 0xa1, 0xac, 0xbf, 0x83, 0xbf, 0xbf, 0xab, 0xbb, 0x85, 0xa0, 0xc0, 0xc3, 0xd5, 0x8c, 0x9c, -0x96, 0x97, 0x93, 0x94, 0x91, 0x92, 0xf7, 0xbf, 0xff, 0x9f, 0xbf, 0x80, 0x8b, 0x9b, 0xbf, 0xbf, -0x87, 0xb7, 0x82, 0x84, 0x89, 0xc2, 0xca, 0xc1, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xd3, 0xd4, -0xbf, 0xd2, 0xda, 0xdb, 0xd9, 0xbf, 0x88, 0x98, 0xaf, 0xbf, 0xbf, 0xbf, 0xb8, 0xbf, 0xbf, 0xbf -}; - -static char *buf = 0; -static int n_buf = 0; - -const char *Fl_Darwin_System_Driver::latin1_to_local(const char *t, int n) -{ - if (n==-1) n = (int)strlen(t); - if (n<=n_buf) { - n_buf = (n + 257) & 0x7fffff00; - if (buf) free(buf); - buf = (char*)malloc(n_buf); - } - const uchar *src = (const uchar*)t; - uchar *dst = (uchar*)buf; - for ( ; n>0; n--) { - uchar c = *src++; - if (c>127) - *dst = latin2roman[c-128]; - else - *dst = c; - } - //*dst = 0; // this would be wrong! - return buf; -} - -const char *Fl_Darwin_System_Driver::local_to_latin1(const char *t, int n) -{ - if (n==-1) n = (int)strlen(t); - if (n<=n_buf) { - n_buf = (n + 257) & 0x7fffff00; - if (buf) free(buf); - buf = (char*)malloc(n_buf); - } - const uchar *src = (const uchar*)t; - uchar *dst = (uchar*)buf; - for ( ; n>0; n--) { - uchar c = *src++; - if (c>127) - *dst++ = roman2latin[c-128]; - else - *dst++ = c; - } - //*dst = 0; // this would be wrong - return buf; -} - -// On Mac OS X, nothing need to be converted. We simply return the -// original pointer. -const char *Fl_Darwin_System_Driver::mac_roman_to_local(const char *t, int) -{ - return t; -} - -// On Mac OS X, nothing need to be converted. We simply return the -// original pointer. -const char *Fl_Darwin_System_Driver::local_to_mac_roman(const char *t, int) -{ - return t; -} - -Fl_Sys_Menu_Bar_Driver *Fl_Darwin_System_Driver::sys_menu_bar_driver() -{ - return Fl_MacOS_Sys_Menu_Bar_Driver::driver(); -} - -// Draw Mac-specific Fl_Tree open/close icons -void Fl_Darwin_System_Driver::tree_draw_expando_button(int x, int y, bool state, bool active) { - fl_color(active ? FL_FOREGROUND_COLOR : FL_INACTIVE_COLOR); - if(state) fl_polygon(x + 3, y, x + 3, y + 11, x + 8, y + 5); // right arrow: ▶ - else fl_polygon(x, y + 3, x + 11, y + 3, x + 5, y + 8); // down arrow: ▼ -} -int Fl_Darwin_System_Driver::tree_connector_style() { - return FL_TREE_CONNECTOR_NONE; -} - - -int Fl_Darwin_System_Driver::filename_relative(char *to, int tolen, const char *dest_dir, const char *base_dir) { - return Fl_System_Driver::filename_relative_(to, tolen, dest_dir, base_dir, false); -} diff --git a/src/drivers/Darwin/fl_macOS_platform_init.cxx b/src/drivers/Darwin/fl_macOS_platform_init.cxx deleted file mode 100644 index fc18e90db..000000000 --- a/src/drivers/Darwin/fl_macOS_platform_init.cxx +++ /dev/null @@ -1,59 +0,0 @@ -// -// macOS-specific code to initialize macOS support. -// -// Copyright 2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include "../Quartz/Fl_Quartz_Copy_Surface_Driver.H" -#include "../Quartz/Fl_Quartz_Graphics_Driver.H" -#include "../Cocoa/Fl_Cocoa_Screen_Driver.H" -#include "../Darwin/Fl_Darwin_System_Driver.H" -#include "../Cocoa/Fl_Cocoa_Window_Driver.H" -#include "../Quartz/Fl_Quartz_Image_Surface_Driver.H" - - -Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h) -{ - return new Fl_Quartz_Copy_Surface_Driver(w, h); -} - - -Fl_Graphics_Driver *Fl_Graphics_Driver::newMainGraphicsDriver() -{ - return new Fl_Quartz_Graphics_Driver(); -} - - -Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver() -{ - return new Fl_Cocoa_Screen_Driver(); -} - - -Fl_System_Driver *Fl_System_Driver::newSystemDriver() -{ - return new Fl_Darwin_System_Driver(); -} - - -Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w) -{ - return new Fl_Cocoa_Window_Driver(w); -} - - -Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, int h, int high_res, Fl_Offscreen off) -{ - return new Fl_Quartz_Image_Surface_Driver(w, h, high_res, off); -} diff --git a/src/drivers/GDI/Fl_Font.H b/src/drivers/GDI/Fl_Font.H deleted file mode 100644 index 3e8b1296f..000000000 --- a/src/drivers/GDI/Fl_Font.H +++ /dev/null @@ -1,43 +0,0 @@ -// -// Font definitions for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Two internal fltk data structures: -// -// Fl_Fontdesc: an entry into the fl_font() table. There is one of these -// for each fltk font number. -// -#ifndef FL_FONT_ -#define FL_FONT_ - -#include -#include "../../Fl_Scalable_Graphics_Driver.H" - -class Fl_GDI_Font_Descriptor : public Fl_Font_Descriptor { -public: - HFONT fid; - int *width[64]; - TEXTMETRIC metr; - int angle; - FL_EXPORT Fl_GDI_Font_Descriptor(const char* fontname, Fl_Fontsize size); -# if HAVE_GL - char glok[64]; -# endif // HAVE_GL - virtual FL_EXPORT ~Fl_GDI_Font_Descriptor(); -}; - -extern FL_EXPORT Fl_Fontdesc *fl_fonts; // the table - -#endif diff --git a/src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.H b/src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.H deleted file mode 100644 index 7eae4c2bc..000000000 --- a/src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.H +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef FL_GDI_COPY_SURFACE_DRIVER_H -#define FL_GDI_COPY_SURFACE_DRIVER_H - -#include -#include - -class Fl_GDI_Copy_Surface_Driver : public Fl_Copy_Surface_Driver { - friend class Fl_Copy_Surface_Driver; -protected: - HDC oldgc; - HDC gc; - Fl_GDI_Copy_Surface_Driver(int w, int h); - ~Fl_GDI_Copy_Surface_Driver(); - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; -}; - -#endif // FL_GDI_COPY_SURFACE_DRIVER_H diff --git a/src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx b/src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx deleted file mode 100644 index c44c0a77b..000000000 --- a/src/drivers/GDI/Fl_GDI_Copy_Surface_Driver.cxx +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include "Fl_GDI_Copy_Surface_Driver.H" -#include -#include "Fl_GDI_Graphics_Driver.H" -#include "../WinAPI/Fl_WinAPI_Screen_Driver.H" -#include -#include - - -Fl_GDI_Copy_Surface_Driver::Fl_GDI_Copy_Surface_Driver(int w, int h) : Fl_Copy_Surface_Driver(w, h) { - driver(Fl_Graphics_Driver::newMainGraphicsDriver()); - oldgc = (HDC)Fl_Surface_Device::surface()->driver()->gc(); - // exact computation of factor from screen units to EnhMetaFile units (0.01 mm) - HDC hdc = GetDC(NULL); - int hmm = GetDeviceCaps(hdc, HORZSIZE); - int hdots = GetDeviceCaps(hdc, HORZRES); - int vmm = GetDeviceCaps(hdc, VERTSIZE); - int vdots = GetDeviceCaps(hdc, VERTRES); - ReleaseDC(NULL, hdc); - float factorw = (100.f * hmm) / hdots; - float factorh = (100.f * vmm) / vdots; - // Global display scaling factor: 1, 1.25, 1.5, 1.75, etc... - float scaling = Fl_Graphics_Driver::default_driver().scale(); - driver()->scale(scaling); - RECT rect; rect.left = 0; rect.top = 0; rect.right = (LONG)((w*scaling) * factorw); rect.bottom = (LONG)((h*scaling) * factorh); - gc = CreateEnhMetaFile (NULL, NULL, &rect, NULL); - if (gc != NULL) { - SetTextAlign(gc, TA_BASELINE|TA_LEFT); - SetBkMode(gc, TRANSPARENT); - } -} - - -Fl_GDI_Copy_Surface_Driver::~Fl_GDI_Copy_Surface_Driver() { - if (oldgc == (HDC)Fl_Surface_Device::surface()->driver()->gc()) oldgc = NULL; - HENHMETAFILE hmf = CloseEnhMetaFile (gc); - if ( hmf != NULL ) { - if ( OpenClipboard (NULL) ){ - EmptyClipboard (); - // put first the vectorial form of the graphics in the clipboard - SetClipboardData (CF_ENHMETAFILE, hmf); - // then put a BITMAP version of the graphics in the clipboard - float scaling = driver()->scale(); - int W = Fl_Scalable_Graphics_Driver::floor(width, scaling), H = Fl_Scalable_Graphics_Driver::floor(height, scaling); - RECT rect = {0, 0, W, H}; - Fl_Image_Surface *surf = new Fl_Image_Surface(W, H); - Fl_Surface_Device::push_current(surf); - fl_color(FL_WHITE); // draw white background - fl_rectf(0, 0, W, H); - PlayEnhMetaFile((HDC)surf->driver()->gc(), hmf, &rect); // draw metafile to offscreen buffer - SetClipboardData(CF_BITMAP, (HBITMAP)surf->offscreen()); - Fl_Surface_Device::pop_current(); - delete surf; - - CloseClipboard (); - } - DeleteEnhMetaFile(hmf); - } - DeleteDC(gc); - Fl_Surface_Device::surface()->driver()->gc(oldgc); - delete driver(); -} - - -void Fl_GDI_Copy_Surface_Driver::set_current() { - driver()->gc(gc); - fl_window = (HWND)1; - Fl_Surface_Device::set_current(); -} - - -void Fl_GDI_Copy_Surface_Driver::translate(int x, int y) { - ((Fl_GDI_Graphics_Driver*)driver())->translate_all(x, y); -} - - -void Fl_GDI_Copy_Surface_Driver::untranslate() { - ((Fl_GDI_Graphics_Driver*)driver())->untranslate_all(); -} diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver.H b/src/drivers/GDI/Fl_GDI_Graphics_Driver.H deleted file mode 100644 index 336fa1ebc..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver.H +++ /dev/null @@ -1,230 +0,0 @@ -// -// Definition of classes Fl_Graphics_Driver, Fl_Surface_Device, Fl_Display_Device -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_GDI_Graphics_Driver.H - \brief Definition of Windows GDI graphics driver. - */ - -#ifndef FL_GDI_GRAPHICS_DRIVER_H -#define FL_GDI_GRAPHICS_DRIVER_H - -#include "../../Fl_Scalable_Graphics_Driver.H" -#include -#include -#include - -#if USE_GDIPLUS -# if defined(_MSC_VER) -# include -# else -# include // for PROPID needed with gcc 4.9.0 but not with 4.9.3 -# endif -# include -#endif - -/** - \brief The Windows-specific graphics driver class. - - This class is implemented only on the Windows platform. -*/ -class Fl_GDI_Graphics_Driver : public Fl_Scalable_Graphics_Driver { -private: - BOOL alpha_blend_(int x, int y, int w, int h, HDC src_gc, int srcx, int srcy, int srcw, int srch); - int depth; // to support translation - POINT *origins; // to support translation - void set_current_() FL_OVERRIDE; - void draw_fixed(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_fixed(Fl_Bitmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void make_unused_color_(unsigned char &r, unsigned char &g, unsigned char &b, int color_count, void **data) FL_OVERRIDE; -protected: - void draw_fixed(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void cache(Fl_RGB_Image *rgb) FL_OVERRIDE; - HDC gc_; - int numcount; - int counts[20]; - uchar *mask_bitmap_; - uchar **mask_bitmap() FL_OVERRIDE {return &mask_bitmap_;} - POINT *long_point; - int style_; -public: - Fl_GDI_Graphics_Driver(); - ~Fl_GDI_Graphics_Driver() FL_OVERRIDE; - int has_feature(driver_feature mask) FL_OVERRIDE { return mask & NATIVE; } - char can_do_alpha_blending() FL_OVERRIDE; - void gc(void *ctxt) FL_OVERRIDE { gc_ = (HDC)ctxt; global_gc(); } - void *gc() FL_OVERRIDE {return gc_;} - - // --- bitmap stuff - static HBITMAP create_bitmask(int w, int h, const uchar *array); // NOT virtual - static HBITMAP calc_HBITMAP_mask(Fl_RGB_Image *mask); - void delete_bitmask(fl_uintptr_t bm) FL_OVERRIDE; - HBITMAP create_alphamask(int w, int h, int d, int ld, const uchar *array); - void draw_unscaled(const char* str, int n, int x, int y) FL_OVERRIDE; - void draw_unscaled(int angle, const char *str, int n, int x, int y) FL_OVERRIDE; - void rtl_draw_unscaled(const char* str, int n, int x, int y) FL_OVERRIDE; - void font_unscaled(Fl_Font face, Fl_Fontsize size) FL_OVERRIDE; - void draw_rgb(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_image_unscaled(const uchar* buf, int X,int Y,int W,int H, int D=3, int L=0) FL_OVERRIDE; - void draw_image_unscaled(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=3) FL_OVERRIDE; - void draw_image_mono_unscaled(const uchar* buf, int X,int Y,int W,int H, int D=1, int L=0) FL_OVERRIDE; - void draw_image_mono_unscaled(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=1) FL_OVERRIDE; - void cache(Fl_Pixmap *img) FL_OVERRIDE; - void uncache_pixmap(fl_uintptr_t p) FL_OVERRIDE; - void cache(Fl_Bitmap *img) FL_OVERRIDE; - void uncache(Fl_RGB_Image *img, fl_uintptr_t &id_, fl_uintptr_t &mask_) FL_OVERRIDE; - double width_unscaled(const char *str, int n) FL_OVERRIDE; - double width_unscaled(unsigned int c) FL_OVERRIDE; - void text_extents_unscaled(const char*, int n, int& dx, int& dy, int& w, int& h) FL_OVERRIDE; - int height_unscaled() FL_OVERRIDE; - int descent_unscaled() FL_OVERRIDE; - Fl_Fontsize size_unscaled() FL_OVERRIDE; -#if ! defined(FL_DOXYGEN) - void copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy); -#endif - void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) FL_OVERRIDE; - void add_rectangle_to_region(Fl_Region r, int x, int y, int w, int h) FL_OVERRIDE; - Fl_Region XRectangleRegion(int x, int y, int w, int h) FL_OVERRIDE; - void XDestroyRegion(Fl_Region r) FL_OVERRIDE; - void translate_all(int x, int y); - void untranslate_all(void); - static HRGN scale_region(HRGN r, float f, Fl_GDI_Graphics_Driver *dr); - void scale(float f) FL_OVERRIDE; - float scale() {return Fl_Graphics_Driver::scale();} -protected: - void transformed_vertex0(float x, float y) FL_OVERRIDE; - void fixloop() FL_OVERRIDE; - void point(int x, int y) FL_OVERRIDE; - void focus_rect(int x, int y, int w, int h) FL_OVERRIDE; - void rect_unscaled(int x, int y, int w, int h) FL_OVERRIDE; - void rectf_unscaled(int x, int y, int w, int h) FL_OVERRIDE; -#if USE_COLORMAP - void colored_rectf(int x, int y, int w, int h, uchar r, uchar g, uchar b) FL_OVERRIDE; -#endif - void line_unscaled(int x, int y, int x1, int y1) FL_OVERRIDE; - void line_unscaled(int x, int y, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void xyline_unscaled(int x, int y, int x1) FL_OVERRIDE; - void yxline_unscaled(int x, int y, int y1) FL_OVERRIDE; - void loop_unscaled(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void loop_unscaled(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; - void polygon_unscaled(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void polygon_unscaled(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; - // --- clipping - void push_clip(int x, int y, int w, int h) FL_OVERRIDE; - int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) FL_OVERRIDE; - int not_clipped(int x, int y, int w, int h) FL_OVERRIDE; - void restore_clip() FL_OVERRIDE; - Fl_Region scale_clip(float f) FL_OVERRIDE; - // --- implementation is in src/fl_vertex.cxx which includes src/cfg_gfx/xxx_rect.cxx - void begin_complex_polygon() FL_OVERRIDE; - void end_points() FL_OVERRIDE; - void end_line() FL_OVERRIDE; - void end_loop() FL_OVERRIDE; - void end_polygon() FL_OVERRIDE; - void end_complex_polygon() FL_OVERRIDE; - void gap() FL_OVERRIDE; - void ellipse_unscaled(double xt, double yt, double rx, double ry) FL_OVERRIDE; - void arc_unscaled(int x, int y, int w, int h, double a1, double a2) FL_OVERRIDE; - void pie_unscaled(int x, int y, int w, int h, double a1, double a2) FL_OVERRIDE; - void line_style_unscaled(int style, int width, char* dashes) FL_OVERRIDE; - void color(Fl_Color c) FL_OVERRIDE; - Fl_Color color() FL_OVERRIDE { return color_; } - void color(uchar r, uchar g, uchar b) FL_OVERRIDE; - void set_color(Fl_Color i, unsigned int c) FL_OVERRIDE; - void free_color(Fl_Color i, int overlay) FL_OVERRIDE; - Fl_Font set_fonts(const char *name) FL_OVERRIDE; - int get_font_sizes(Fl_Font fnum, int*& sizep) FL_OVERRIDE; - const char* get_font_name(Fl_Font fnum, int* ap) FL_OVERRIDE; - const char *font_name(int num) FL_OVERRIDE; - void font_name(int num, const char *name) FL_OVERRIDE; - void global_gc() FL_OVERRIDE; - void overlay_rect(int x, int y, int w , int h) FL_OVERRIDE; - void cache_size(Fl_Image *img, int &width, int &height) FL_OVERRIDE; - void* change_pen_width(int width) FL_OVERRIDE; - void reset_pen_width(void *data) FL_OVERRIDE; -}; - - -/** - The graphics driver used when printing on Windows. - - This class is implemented only on the Windows platform. - It is extremely similar to Fl_GDI_Graphics_Driver. -*/ -class Fl_GDI_Printer_Graphics_Driver : public Fl_GDI_Graphics_Driver { -private: - typedef BOOL (WINAPI* transparent_f_type) (HDC,int,int,int,int,HDC,int,int,int,int,UINT); - transparent_f_type TransparentBlt(); -public: - int has_feature(driver_feature mask) FL_OVERRIDE { return mask & (NATIVE | PRINTER); } - void draw_pixmap(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_bitmap(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen bitmap, int srcx, int srcy) FL_OVERRIDE; -}; - -#if USE_GDIPLUS - -class Fl_GDIplus_Graphics_Driver : public Fl_GDI_Graphics_Driver { - friend class Fl_Graphics_Driver; -private: - Gdiplus::Color gdiplus_color_; - Gdiplus::Pen *pen_; - Gdiplus::SolidBrush *brush_; - // The code below ensures that a connection to GDIplus is only made once, and that the - // matching connection shutdown is also done exactly once. - enum { - STATE_CLOSED = 0, // no connection, token is invalid - STATE_STARTUP, // attempt to start up, avoid recursions for whatever reason - STATE_OPEN, // connection was successful and the token is valid - STATE_SHUTDOWN // shutting down the gdi connection, avoid possible recursion - }; - static int gdiplus_state_; // reflect the state of the GDIplus driver connection - static ULONG_PTR gdiplus_token_; // the token that GDIplus gives to us -public: - Fl_GDIplus_Graphics_Driver(); - virtual ~Fl_GDIplus_Graphics_Driver(); - bool active; - static void shutdown(void); - void color(Fl_Color c) FL_OVERRIDE; - Fl_Color color() FL_OVERRIDE { return color_; } - void color(uchar r, uchar g, uchar b) FL_OVERRIDE; - void line(int x, int y, int x1, int y1) FL_OVERRIDE; - void line(int x, int y, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void loop(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; - void polygon(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; - void line_style(int style, int width, char* dashes) FL_OVERRIDE; - void arc_unscaled(int x, int y, int w, int h, double a1, double a2) FL_OVERRIDE; - void pie_unscaled(int x, int y, int w, int h, double a1, double a2) FL_OVERRIDE; - void draw_circle(int x, int y, int d, Fl_Color c) FL_OVERRIDE; - void transformed_vertex(double xf, double yf) FL_OVERRIDE; - void vertex(double x,double y) FL_OVERRIDE; - void end_points() FL_OVERRIDE; - void end_line() FL_OVERRIDE; - void end_loop() FL_OVERRIDE; - void end_polygon() FL_OVERRIDE; - void end_complex_polygon() FL_OVERRIDE; - void circle(double x, double y, double r) FL_OVERRIDE; - void antialias(int state) FL_OVERRIDE; - int antialias() FL_OVERRIDE; -}; - -#endif // USE_GDIPLUS - -#endif // FL_GDI_GRAPHICS_DRIVER_H diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver.cxx deleted file mode 100644 index 97b3244d1..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver.cxx +++ /dev/null @@ -1,322 +0,0 @@ -// -// Rectangle drawing routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include -#include "Fl_GDI_Graphics_Driver.H" -#include -#include -#include -#include "../../Fl_Screen_Driver.H" -#include "Fl_Font.H" - -#if USE_GDIPLUS - -Fl_GDIplus_Graphics_Driver::Fl_GDIplus_Graphics_Driver() : Fl_GDI_Graphics_Driver() { - if (!fl_current_xmap) color(FL_BLACK); - pen_ = new Gdiplus::Pen(gdiplus_color_, 1); - pen_->SetLineJoin(Gdiplus::LineJoinRound); - pen_->SetStartCap(Gdiplus::LineCapFlat); - pen_->SetEndCap(Gdiplus::LineCapFlat); - brush_ = new Gdiplus::SolidBrush(gdiplus_color_); - active = true; -} - -Fl_GDIplus_Graphics_Driver::~Fl_GDIplus_Graphics_Driver() { - delete pen_; - delete brush_; -} - -void Fl_GDIplus_Graphics_Driver::antialias(int state) { - active = state; -} - -int Fl_GDIplus_Graphics_Driver::antialias() { - return active; -} - -void Fl_GDIplus_Graphics_Driver::draw_circle(int x, int y, int d, Fl_Color c) { - Fl_Graphics_Driver::draw_circle(x, y, d, c); -} - -int Fl_GDIplus_Graphics_Driver::gdiplus_state_ = Fl_GDIplus_Graphics_Driver::STATE_CLOSED; -ULONG_PTR Fl_GDIplus_Graphics_Driver::gdiplus_token_ = 0; - -void Fl_GDIplus_Graphics_Driver::shutdown() { - if (gdiplus_state_ == STATE_OPEN) { - gdiplus_state_ = STATE_SHUTDOWN; - Gdiplus::GdiplusShutdown(Fl_GDIplus_Graphics_Driver::gdiplus_token_); - gdiplus_token_ = 0; - gdiplus_state_ = STATE_CLOSED; - } else if (gdiplus_state_ == STATE_CLOSED) { -// Fl::warning("Fl_GDIplus_Graphics_Driver::shutdown() called, but driver is closed."); - } else if (gdiplus_state_ == STATE_SHUTDOWN) { -// Fl::warning("Fl_GDIplus_Graphics_Driver::shutdown() called recursively."); - } else if (gdiplus_state_ == STATE_STARTUP) { -// Fl::warning("Fl_GDIplus_Graphics_Driver::shutdown() called while driver is starting up."); - } -} -#endif - -// Code used to switch output to an off-screen window. See macros in -// win32.H which save the old state in local variables. - -typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION; -typedef BOOL (WINAPI* fl_alpha_blend_func) -(HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION); -static fl_alpha_blend_func fl_alpha_blend = NULL; -static FL_BLENDFUNCTION blendfunc = { 0, 0, 255, 1}; - -/* Reference to the current device context - For back-compatibility only. The preferred procedure to get this reference is - Fl_Surface_Device::surface()->driver()->gc(). - */ -HDC fl_gc = 0; - - -HDC fl_win32_gc() { return fl_gc; } - - -Fl_GDI_Graphics_Driver::Fl_GDI_Graphics_Driver() { - mask_bitmap_ = NULL; - gc_ = NULL; - long_point = NULL; - depth = -1; - origins = NULL; - style_ = FL_SOLID; -} - -Fl_GDI_Graphics_Driver::~Fl_GDI_Graphics_Driver() { - if (long_point) free(long_point); - delete[] origins; -} - -void Fl_GDI_Graphics_Driver::global_gc() -{ - fl_gc = (HDC)gc(); -} - -/* - * This function checks if the version of Windows that we - * curently run on supports alpha blending for bitmap transfers - * and finds the required function if so. - */ -char Fl_GDI_Graphics_Driver::can_do_alpha_blending() { - static char been_here = 0; - static char can_do = 0; - // do this test only once - if (been_here) return can_do; - been_here = 1; - // load the library that implements alpha blending - HMODULE hMod = LoadLibrary("MSIMG32.DLL"); - // give up if that doesn't exist (Win95?) - if (!hMod) return 0; - // now find the blending function inside that dll - fl_alpha_blend = (fl_alpha_blend_func)GetProcAddress(hMod, "AlphaBlend"); - // give up if we can't find it (Win95) - if (!fl_alpha_blend) return 0; - // we have the call, but does our display support alpha blending? - // get the desktop's device context - HDC dc = GetDC(0L); - if (!dc) return 0; - // check the device capabilities flags. However GetDeviceCaps - // does not return anything useful, so we have to do it manually: - - HBITMAP bm = CreateCompatibleBitmap(dc, 1, 1); - HDC new_gc = CreateCompatibleDC(dc); - int save = SaveDC(new_gc); - SelectObject(new_gc, bm); - /*COLORREF set = */ SetPixel(new_gc, 0, 0, 0x01010101); - BOOL alpha_ok = fl_alpha_blend(dc, 0, 0, 1, 1, new_gc, 0, 0, 1, 1, blendfunc); - RestoreDC(new_gc, save); - DeleteDC(new_gc); - DeleteObject(bm); - ReleaseDC(0L, dc); - - if (alpha_ok) can_do = 1; - return can_do; -} - -HDC fl_makeDC(HBITMAP bitmap) { - HDC new_gc = CreateCompatibleDC((HDC)Fl_Graphics_Driver::default_driver().gc()); - SetTextAlign(new_gc, TA_BASELINE|TA_LEFT); - SetBkMode(new_gc, TRANSPARENT); -#if USE_COLORMAP - if (fl_palette) SelectPalette(new_gc, fl_palette, FALSE); -#endif - SelectObject(new_gc, bitmap); - return new_gc; -} - -void Fl_GDI_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen bitmap, int srcx, int srcy) { - x = int(x * scale()); y = int(y * scale()); w = int(w * scale()); h = int(h * scale()); - srcx = int(srcx * scale()); srcy = int(srcy * scale()); - if (srcx < 0) {w += srcx; x -= srcx; srcx = 0;} - if (srcy < 0) {h += srcy; y -= srcy; srcy = 0;} - int off_width, off_height; - Fl::screen_driver()->offscreen_size(bitmap, off_width, off_height); - if (srcx + w >= off_width) {w = off_width - srcx;} - if (srcy + h >= off_height) {h = off_height - srcy;} - if (w <= 0 || h <= 0) return; - HDC new_gc = CreateCompatibleDC(gc_); - int save = SaveDC(new_gc); - SelectObject(new_gc, (HBITMAP)bitmap); - BitBlt(gc_, x, y, w, h, new_gc, srcx, srcy, SRCCOPY); - RestoreDC(new_gc, save); - DeleteDC(new_gc); -} - -void Fl_GDI_Printer_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen bitmap, int srcx, int srcy) { - Fl_Graphics_Driver::copy_offscreen(x, y, w, h, bitmap, srcx, srcy); -} - -BOOL Fl_GDI_Graphics_Driver::alpha_blend_(int x, int y, int w, int h, HDC src_gc, int srcx, int srcy, int srcw, int srch) { - return fl_alpha_blend(gc_, x, y, w, h, src_gc, srcx, srcy, srcw, srch, blendfunc); -} - -#if ! defined(FL_DOXYGEN) -void Fl_GDI_Graphics_Driver::copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) { - HDC new_gc = CreateCompatibleDC(gc_); - int save = SaveDC(new_gc); - SelectObject(new_gc, bitmap); - BOOL alpha_ok = 0; - // first try to alpha blend - if ( fl_can_do_alpha_blending() ) { - alpha_ok = alpha_blend_(x, y, w, h, new_gc, srcx, srcy, w, h); - } - // if that failed (it shouldn't), still copy the bitmap over, but now alpha is 1 - if (!alpha_ok) { - BitBlt(gc_, x, y, w, h, new_gc, srcx, srcy, SRCCOPY); - } - RestoreDC(new_gc, save); - DeleteDC(new_gc); -} - -void Fl_GDI_Graphics_Driver::translate_all(int x, int y) { - const int stack_height = 10; - if (depth == -1) { - origins = new POINT[stack_height]; - depth = 0; - } - if (depth >= stack_height) { - Fl::warning("Fl_Copy/Image_Surface: translate stack overflow!"); - depth = stack_height - 1; - } - GetWindowOrgEx((HDC)gc(), origins+depth); - SetWindowOrgEx((HDC)gc(), int(origins[depth].x - x*scale()), int(origins[depth].y - y*scale()), NULL); - depth++; -} - -void Fl_GDI_Graphics_Driver::untranslate_all() { - if (depth > 0) depth--; - SetWindowOrgEx((HDC)gc(), origins[depth].x, origins[depth].y, NULL); -} -#endif - -void Fl_GDI_Graphics_Driver::add_rectangle_to_region(Fl_Region r, int X, int Y, int W, int H) { - HRGN R = (HRGN)XRectangleRegion(X, Y, W, H); - CombineRgn((HRGN)r, (HRGN)r, R, RGN_OR); - XDestroyRegion(R); -} - -void Fl_GDI_Graphics_Driver::transformed_vertex0(float x, float y) { - if (!n || x != long_point[n-1].x || y != long_point[n-1].y) { - if (n >= p_size) { - p_size = long_point ? 2*p_size : 16; - long_point = (POINT*)realloc((void*)long_point, p_size*sizeof(*long_point)); - } - long_point[n].x = LONG(x); - long_point[n].y = LONG(y); - n++; - } -} - -void Fl_GDI_Graphics_Driver::fixloop() { // remove equal points from closed path - while (n>2 && long_point[n-1].x == long_point[0].x && long_point[n-1].y == long_point[0].y) n--; -} - -Fl_Region Fl_GDI_Graphics_Driver::XRectangleRegion(int x, int y, int w, int h) { - if (Fl_Surface_Device::surface() == Fl_Display_Device::display_device()) return CreateRectRgn(x,y,x+w,y+h); - // because rotation may apply, the rectangle becomes a polygon in device coords - POINT pt[4] = { {x, y}, {x + w, y}, {x + w, y + h}, {x, y + h} }; - LPtoDP((HDC)fl_graphics_driver->gc(), pt, 4); - return CreatePolygonRgn(pt, 4, ALTERNATE); -} - -void Fl_GDI_Graphics_Driver::XDestroyRegion(Fl_Region r) { - DeleteObject((HRGN)r); -} - - -void Fl_GDI_Graphics_Driver::scale(float f) { - if (f != scale()) { - size_ = 0; - Fl_Graphics_Driver::scale(f); - color(FL_BLACK); - line_style(FL_SOLID); // scale also default line width - } -} - - -/* Rescale region r with factor f and returns the scaled region. - Region r is returned unchanged if r is null or f is 1. - */ -HRGN Fl_GDI_Graphics_Driver::scale_region(HRGN r, float f, Fl_GDI_Graphics_Driver *dr) { - if (r && f != 1) { - DWORD size = GetRegionData(r, 0, NULL); - RGNDATA *pdata = (RGNDATA*)malloc(size); - GetRegionData(r, size, pdata); - POINT pt = {0, 0}; - if (dr && dr->depth >= 1) { // account for translation - GetWindowOrgEx((HDC)dr->gc(), &pt); - pt.x = int(pt.x * (f - 1)); - pt.y = int(pt.y * (f - 1)); - } - RECT *rects = (RECT*)&(pdata->Buffer); - for (DWORD i = 0; i < pdata->rdh.nCount; i++) { - int x = Fl_Scalable_Graphics_Driver::floor(rects[i].left, f) + pt.x; - int y = Fl_Scalable_Graphics_Driver::floor(rects[i].top, f) + pt.y; - RECT R2; - R2.left = x; - R2.top = y; - R2.right = Fl_Scalable_Graphics_Driver::floor(rects[i].right, f) + pt.x - x + R2.left; - R2.bottom = Fl_Scalable_Graphics_Driver::floor(rects[i].bottom, f) + pt.y - y + R2.top; - rects[i] = R2; - } - r = ExtCreateRegion(NULL, size, pdata); - free(pdata); - } - return r; -} - - -Fl_Region Fl_GDI_Graphics_Driver::scale_clip(float f) { - HRGN r = (HRGN)rstack[rstackptr]; - HRGN r2 = scale_region(r, f, this); - return (r == r2 ? NULL : (rstack[rstackptr] = r2, r)); -} - -void Fl_GDI_Graphics_Driver::set_current_() { - restore_clip(); -} - -void Fl_GDI_Graphics_Driver::cache_size(Fl_Image *img, int &width, int &height) -{ - float s = scale(); - width = (s == int(s) ? width * int(s) : floor(width+1)); - height = (s == int(s) ? height * int(s) : floor(height+1)); - cache_size_finalize(img, width, height); -} diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_arci.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_arci.cxx deleted file mode 100644 index 0c1e90064..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_arci.cxx +++ /dev/null @@ -1,89 +0,0 @@ -// -// Arc (integer) drawing functions for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_GDI_Graphics_Driver_arci.cxx - \brief Utility functions for drawing circles using integers -*/ - -// "integer" circle drawing functions. These draw the limited -// circle types provided by X and NT graphics. The advantage of -// these is that small ones draw quite nicely (probably due to stored -// hand-drawn bitmaps of small circles!) and may be implemented by -// hardware and thus are fast. - -#include "Fl_GDI_Graphics_Driver.H" - -#include -#include - -void Fl_GDI_Graphics_Driver::arc_unscaled(int x, int y, int w, int h, double a1, double a2) { - if (w <= 0 || h <= 0) return; - w++; h++; - int xa = int( x+w/2+int(w*cos(a1/180.0*M_PI)) ); - int ya = int( y+h/2-int(h*sin(a1/180.0*M_PI)) ); - int xb = int( x+w/2+int(w*cos(a2/180.0*M_PI)) ); - int yb = int( y+h/2-int(h*sin(a2/180.0*M_PI)) ); - if (fabs(a1 - a2) < 90) { - if (xa == xb && ya == yb) SetPixel(gc_, xa, ya, fl_RGB()); - else Arc(gc_, int(x), int(y), int(x+w), int(y+h), xa, ya, xb, yb); - } else Arc(gc_, int(x), int(y), int(x+w), int(y+h), xa, ya, xb, yb); -} - -void Fl_GDI_Graphics_Driver::pie_unscaled(int x, int y, int w, int h, double a1, double a2) { - if (w <= 0 || h <= 0) return; - if (a1 == a2) return; - x++; y++; w--; h--; - if (scale() >= 3) {x++; y++; w-=2; h-=2;} - int xa = int( x+w/2+int(w*cos(a1/180.0*M_PI)) ); - int ya = int( y+h/2-int(h*sin(a1/180.0*M_PI)) ); - int xb = int( x+w/2+int(w*cos(a2/180.0*M_PI)) ); - int yb = int( y+h/2-int(h*sin(a2/180.0*M_PI)) ); - SelectObject(gc_, fl_brush()); - if (fabs(a1 - a2) < 90) { - if (xa == xb && ya == yb) { - MoveToEx(gc_, int(x+w/2), int(y+h/2), 0L); - LineTo(gc_, xa, ya); - SetPixel(gc_, xa, ya, fl_RGB()); - } else Pie(gc_, int(x), int(y), int(x+w), int(y+h), xa, ya, xb, yb); - } else Pie(gc_, int(x), int(y), int(x+w), int(y+h), xa, ya, xb, yb); -} - -#if USE_GDIPLUS - -void Fl_GDIplus_Graphics_Driver::arc_unscaled(int x, int y, int w, int h, double a1, double a2) { - if (w <= 0 || h <= 0) return; - if (!active) return Fl_GDI_Graphics_Driver::arc_unscaled(x, y, w, h, a1, a2); - Gdiplus::Graphics graphics_(gc_); - pen_->SetColor(gdiplus_color_); - Gdiplus::REAL oldw = pen_->GetWidth(); - Gdiplus::REAL new_w = (line_width_ <= scale() ? 1 : line_width_) * scale(); - pen_->SetWidth(new_w); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.DrawArc(pen_, x, y, w, h, Gdiplus::REAL(-a1), Gdiplus::REAL(a1-a2)); - pen_->SetWidth(oldw); -} - -void Fl_GDIplus_Graphics_Driver::pie_unscaled(int x, int y, int w, int h, double a1, double a2) { - if (w <= 0 || h <= 0) return; - if (!active) return Fl_GDI_Graphics_Driver::pie_unscaled(x, y, w, h, a1, a2); - Gdiplus::Graphics graphics_(gc_); - brush_->SetColor(gdiplus_color_); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.FillPie(brush_, x, y, w, h, Gdiplus::REAL(-a1), Gdiplus::REAL(a1-a2)); -} - -#endif diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_color.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_color.cxx deleted file mode 100644 index c05a255d0..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_color.cxx +++ /dev/null @@ -1,259 +0,0 @@ -// -// MSWidnows' GDI color functions for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// The fltk "colormap". This allows ui colors to be stored in 8-bit -// locations, and provides a level of indirection so that global color -// changes can be made. Not to be confused with the X colormap, which -// I try to hide completely. - -#include "Fl_GDI_Graphics_Driver.H" - -#include -#include -#include -#include - -// FIXME: all the global functions in this file should probably be protected -// members of the driver class. Starting with 1.4 we will allow multiple drivers -// to co-exist, creating conflicts with multipe mapping. - -// FIXME: maybe we can forget about color mapping and assume RGB? -// FIXME: ... but for now we still have it ... -extern unsigned fl_cmap[256]; // defined in fl_color.cxx - -// Translations to win32 data structures: -Fl_XMap fl_xmap[256]; - -Fl_XMap* fl_current_xmap; - -HPALETTE fl_palette; -static HGDIOBJ tmppen=0; -static HPEN savepen=0; - -void fl_cleanup_pens(void) { - for (int i=0; i<256; i++) { - if (fl_xmap[i].pen) DeleteObject(fl_xmap[i].pen); - } -} - -void fl_save_pen(void) { - if(!tmppen) tmppen = CreatePen(PS_SOLID, 1, 0); - savepen = (HPEN)SelectObject((HDC)fl_graphics_driver->gc(), tmppen); -} - -void fl_restore_pen(void) { - if (savepen) SelectObject((HDC)fl_graphics_driver->gc(), savepen); - DeleteObject(tmppen); - tmppen = 0; - savepen = 0; -} - -static void clear_xmap(Fl_XMap& xmap) { - if (xmap.pen) { - HDC gc = (HDC)fl_graphics_driver->gc(); - HGDIOBJ tmppen = GetStockObject(BLACK_PEN); - HGDIOBJ oldpen = SelectObject(gc, tmppen); // Push out the current pen of the gc - if(oldpen != xmap.pen) SelectObject(gc, oldpen); // Put it back if it is not the one we are about to delete - DeleteObject((HGDIOBJ)(xmap.pen)); - xmap.pen = 0; - xmap.brush = -1; - } -} - -static void set_xmap(Fl_XMap& xmap, COLORREF c, int lw) { - xmap.rgb = c; - if (xmap.pen) { - HDC gc = (HDC)fl_graphics_driver->gc(); - HGDIOBJ oldpen = SelectObject(gc,GetStockObject(BLACK_PEN)); // replace current pen with safe one - if (oldpen != xmap.pen)SelectObject(gc,oldpen); // if old one not xmap.pen, need to put it back - DeleteObject(xmap.pen); // delete pen - } -// xmap.pen = CreatePen(PS_SOLID, 1, xmap.rgb); // get a pen into xmap.pen - LOGBRUSH penbrush = {BS_SOLID, xmap.rgb, 0}; - xmap.pen = ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT | PS_JOIN_ROUND, lw, &penbrush, 0, 0); - xmap.pwidth = lw; - xmap.brush = -1; -} - -void Fl_GDI_Graphics_Driver::color(Fl_Color i) { - if (i & 0xffffff00) { - unsigned rgb = (unsigned)i; - color((uchar)(rgb >> 24), (uchar)(rgb >> 16), (uchar)(rgb >> 8)); - } else { - Fl_Graphics_Driver::color(i); - Fl_XMap &xmap = fl_xmap[i]; - int tw = line_width_ ? line_width_ : int(scale()); if (!tw) tw = 1; - if (!xmap.pen || xmap.pwidth != tw) { -#if USE_COLORMAP - if (fl_palette) { - set_xmap(xmap, PALETTEINDEX(i), tw); - } else { -#endif - unsigned c = fl_cmap[i]; - set_xmap(xmap, RGB(uchar(c>>24), uchar(c>>16), uchar(c>>8)), tw); -#if USE_COLORMAP - } -#endif - } - fl_current_xmap = ⟼ - SelectObject(gc_, (HGDIOBJ)(xmap.pen)); - } -} - -void Fl_GDI_Graphics_Driver::color(uchar r, uchar g, uchar b) { - static Fl_XMap xmap; - COLORREF c = RGB(r,g,b); - Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) ); - int tw = line_width_ ? line_width_ : int(scale()); if (!tw) tw = 1; - if (!xmap.pen || c != xmap.rgb || tw != xmap.pwidth) { - clear_xmap(xmap); - set_xmap(xmap, c, tw); - } - fl_current_xmap = ⟼ - SelectObject(gc_, (HGDIOBJ)(xmap.pen)); -} - -HBRUSH fl_brush() { - return fl_brush_action(0); -} - -HBRUSH fl_brush_action(int action) { - Fl_XMap *xmap = fl_current_xmap; - HDC gc = (HDC)fl_graphics_driver->gc(); - // Wonko: we use some statistics to cache only a limited number - // of brushes: -#define FL_N_BRUSH 16 - static struct Fl_Brush { - HBRUSH brush; - unsigned short usage; - Fl_XMap* backref; - } brushes[FL_N_BRUSH]; - - if (action) { - SelectObject(gc, GetStockObject(BLACK_BRUSH)); // Load stock object - for (int i=0; ibrush; // find the associated brush - if (i != -1) { // if the brush was allready allocated - if (brushes[i].brush == NULL) goto CREATE_BRUSH; - if ( (++brushes[i].usage) > 32000 ) { // keep a usage statistic - for (int j=0; j16000) - brushes[j].usage -= 16000; - else - brushes[j].usage = 0; - } - } - return brushes[i].brush; - } else { - int umin = 32000, imin = 0; - for (i=0; ibrush = -1; - } -CREATE_BRUSH: - brushes[i].brush = CreateSolidBrush(xmap->rgb); - brushes[i].usage = 0; - brushes[i].backref = xmap; - xmap->brush = i; - return brushes[i].brush; -} - -void Fl_GDI_Graphics_Driver::free_color(Fl_Color i, int overlay) { - if (overlay) return; // do something about GL overlay? - clear_xmap(fl_xmap[i]); -} - -void Fl_GDI_Graphics_Driver::set_color(Fl_Color i, unsigned c) { - if (fl_cmap[i] != c) { - clear_xmap(fl_xmap[i]); - fl_cmap[i] = c; - } -} - -#if USE_COLORMAP - -// 'fl_select_palette()' - Make a color palette for 8-bit displays if necessary -// Thanks to Michael Sweet @ Easy Software Products for this - -HPALETTE -fl_select_palette(void) -{ - static char beenhere; - HDC gc = (HDC)fl_graphics_driver->gc(); - if (!beenhere) { - beenhere = 1; - - int nColors = GetDeviceCaps(gc, SIZEPALETTE); - if (nColors <= 0 || nColors > 256) return NULL; - // this will try to work on < 256 color screens, but will probably - // come out quite badly. - - // I lamely try to get this variable-sized object allocated on stack: - ulong foo[(sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY))/sizeof(ulong)+1]; - LOGPALETTE *pPal = (LOGPALETTE*)foo; - - pPal->palVersion = 0x300; - pPal->palNumEntries = nColors; - - // Build 256 colors from the standard FLTK colormap... - - for (int i = 0; i < nColors; i ++) { - pPal->palPalEntry[i].peRed = (fl_cmap[i] >> 24) & 255; - pPal->palPalEntry[i].peGreen = (fl_cmap[i] >> 16) & 255; - pPal->palPalEntry[i].peBlue = (fl_cmap[i] >> 8) & 255; - pPal->palPalEntry[i].peFlags = 0; - }; - - // Create the palette: - fl_palette = CreatePalette(pPal); - } - if (fl_palette) { - SelectPalette(gc, fl_palette, FALSE); - RealizePalette(gc); - } - return fl_palette; -} - -#endif - -#if USE_GDIPLUS -void Fl_GDIplus_Graphics_Driver::color(uchar r, uchar g, uchar b) { - Fl_GDI_Graphics_Driver::color(r, g, b); - gdiplus_color_.SetFromCOLORREF(fl_RGB()); -} - -void Fl_GDIplus_Graphics_Driver::color(Fl_Color i) { - Fl_GDI_Graphics_Driver::color(i); - gdiplus_color_.SetFromCOLORREF(fl_RGB()); -} -#endif // USE_GDIPLUS diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_font.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_font.cxx deleted file mode 100644 index 49111f10e..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_font.cxx +++ /dev/null @@ -1,678 +0,0 @@ -// -// Windows font utilities for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -/* We require Windows 2000 features such as GetGlyphIndices */ -#if !defined(WINVER) || (WINVER < 0x0500) -# ifdef WINVER -# undef WINVER -# endif -# define WINVER 0x0500 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# ifdef _WIN32_WINNT -# undef _WIN32_WINNT -# endif -# define _WIN32_WINNT 0x0500 -#endif - -// Select fonts from the FLTK font table. -#include "Fl_GDI_Graphics_Driver.H" -#include "../../flstring.h" -#include -#include -#include -#include "Fl_Font.H" - -#include -#include -#include - -// This function fills in the FLTK font table with all the fonts that -// are found on the X server. It tries to place the fonts into families -// and to sort them so the first 4 in a family are normal, bold, italic, -// and bold italic. -#include -#ifdef __CYGWIN__ -# include -#endif - -// Bug: older versions calculated the value for *ap as a side effect of -// making the name, and then forgot about it. To avoid having to change -// the header files I decided to store this value in the last character -// of the font name array. -#define ENDOFBUFFER 127 // sizeof(Fl_Font.fontname)-1 - -// turn a stored font name into a pretty name: -const char* Fl_GDI_Graphics_Driver::get_font_name(Fl_Font fnum, int* ap) { - Fl_Fontdesc *f = fl_fonts + fnum; - if (!f->fontname[0]) { - const char* p = f->name; - if (!p || !*p) {if (ap) *ap = 0; return "";} - int type; - switch (*p) { - case 'B': type = FL_BOLD; break; - case 'I': type = FL_ITALIC; break; - case 'P': type = FL_BOLD | FL_ITALIC; break; - default: type = 0; break; - } - strlcpy(f->fontname, p+1, ENDOFBUFFER); - if (type & FL_BOLD) strlcat(f->fontname, " bold", ENDOFBUFFER); - if (type & FL_ITALIC) strlcat(f->fontname, " italic", ENDOFBUFFER); - f->fontname[ENDOFBUFFER] = (char)type; - } - if (ap) *ap = f->fontname[ENDOFBUFFER]; - return f->fontname; -} - -static int fl_free_font = FL_FREE_FONT; - -// helper function for `enumcbw()` to avoid code repetition -// input: -// ft: font "type", i.e. ' ', 'B', 'I', or 'P' -// fn: font name whose first byte is overwritten and then stored - -static void set_font_name(const char ft, char *fn) { - fn[0] = ft; - Fl::set_font((Fl_Font)(fl_free_font++), fl_strdup(fn)); -} - -// Callback for EnumFontFamiliesW(): -// return 1 to continue, 0 to stop enumeration - -static int CALLBACK -enumcbw(CONST LOGFONTW *lpelf, - CONST TEXTMETRICW * /* lpntm */, - DWORD /* FontType */, - LPARAM p) { - if (!p && lpelf->lfCharSet != ANSI_CHARSET) return 1; - char *fn = nullptr; // FLTK font name - unsigned lw = (unsigned)wcslen(lpelf->lfFaceName); - unsigned dstlen = fl_utf8fromwc(fn, 0, (wchar_t*)lpelf->lfFaceName, lw); // measure the string - fn = (char*)malloc((size_t)dstlen + 2); // "?" + name + NUL - if (!fn) return 1; - fn[0] = ' '; - dstlen = fl_utf8fromwc(fn+1, dstlen+1, (wchar_t*)lpelf->lfFaceName, lw); // convert the string - fn[dstlen + 1] = 0; - // skip if it is one of our built-in fonts - for (int i = 0; i < FL_FREE_FONT; i++) { - if (!strcmp(Fl::get_font_name((Fl_Font)i), fn+1)) { - free(fn); - return 1; - } - } - set_font_name(' ', fn); - if (lpelf->lfWeight <= 400) - set_font_name('B', fn); - set_font_name('I', fn); - if (lpelf->lfWeight <= 400) - set_font_name('P', fn); - free(fn); - return 1; -} /* enumcbw */ - -Fl_Font Fl_GDI_Graphics_Driver::set_fonts(const char* xstarname) { - HDC gc = (HDC)fl_graphics_driver->gc(); - if (fl_free_font == FL_FREE_FONT) {// if not already been called - if (!gc) gc = fl_GetDC(0); - - EnumFontFamiliesW(gc, NULL, (FONTENUMPROCW)enumcbw, xstarname != 0); - - } - return (Fl_Font)fl_free_font; -} - - -static int nbSize; -static int cyPerInch; -static int sizes[128]; -static int CALLBACK - -EnumSizeCbW(CONST LOGFONTW * /*lpelf*/, - CONST TEXTMETRICW *lpntm, - DWORD fontType, - LPARAM /*p*/) { - if ((fontType & RASTER_FONTTYPE) == 0) { - sizes[0] = 0; - nbSize = 1; - - // Scalable font - return 0; - } - - int add = lpntm->tmHeight - lpntm->tmInternalLeading; - add = MulDiv(add, 72, cyPerInch); - - int start = 0; - while ((start < nbSize) && (sizes[start] < add)) { - start++; - } - - if ((start < nbSize) && (sizes[start] == add)) { - return 1; - } - - for (int i=nbSize; i>start; i--) sizes[i] = sizes[i - 1]; - - sizes[start] = add; - nbSize++; - - // Stop enum if buffer overflow - return nbSize < 128; -} - - -int Fl_GDI_Graphics_Driver::get_font_sizes(Fl_Font fnum, int*& sizep) { - nbSize = 0; - Fl_Fontdesc *s = fl_fonts+fnum; - if (!s->name) s = fl_fonts; // empty slot in table, use entry 0 - - HDC gc = (HDC)fl_graphics_driver->gc(); - if (!gc) gc = fl_GetDC(0); - cyPerInch = GetDeviceCaps(gc, LOGPIXELSY); - if (cyPerInch < 1) cyPerInch = 1; - -// int l = fl_utf_nb_char((unsigned char*)s->name+1, strlen(s->name+1)); -// unsigned short *b = (unsigned short*) malloc((l + 1) * sizeof(short)); -// fl_utf2unicode((unsigned char*)s->name+1, l, (wchar_t*)b); - const char *nm = (const char*)s->name+1; - size_t len = strlen(s->name+1); - unsigned l = fl_utf8toUtf16(nm, (unsigned) len, NULL, 0); // Pass NULL to query length required - unsigned short *b = (unsigned short*) malloc((l + 1) * sizeof(short)); - l = fl_utf8toUtf16(nm, (unsigned) len, b, (l+1)); // Now do the conversion - b[l] = 0; - EnumFontFamiliesW(gc, (WCHAR*)b, (FONTENUMPROCW)EnumSizeCbW, 0); - free(b); - - sizep = sizes; - return nbSize; -} - -const char *Fl_GDI_Graphics_Driver::font_name(int num) { - return fl_fonts[num].name; -} - -void Fl_GDI_Graphics_Driver::font_name(int num, const char *name) { - Fl_Fontdesc *s = fl_fonts + num; - if (s->name) { - if (!strcmp(s->name, name)) {s->name = name; return;} - for (Fl_Font_Descriptor* f = s->first; f;) { - Fl_Font_Descriptor* n = f->next; delete f; f = n; - } - s->first = 0; - } - s->name = name; - s->fontname[0] = 0; - s->first = 0; -} - - -static int fl_angle_ = 0; -// Unicode string buffer -static unsigned short *wstr = NULL; -static int wstr_len = 0; - -#ifndef FL_DOXYGEN -Fl_GDI_Font_Descriptor::Fl_GDI_Font_Descriptor(const char* name, Fl_Fontsize fsize) : Fl_Font_Descriptor(name,fsize) { - int weight = FW_NORMAL; - int italic = 0; - switch (*name++) { - case 'I': italic = 1; break; - case 'P': italic = 1; - case 'B': weight = FW_BOLD; break; - case ' ': break; - default: name--; - } - int wn = fl_utf8toUtf16(name, (unsigned int)strlen(name), wstr, wstr_len); - if (wn >= wstr_len) { - wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1)); - wstr_len = wn + 1; - wn = fl_utf8toUtf16(name, (unsigned int)strlen(name), wstr, wstr_len); - } - - fid = CreateFontW( - -fsize, // negative makes it use "char size" - 0, // logical average character width - fl_angle_*10, // angle of escapement - fl_angle_*10, // base-line orientation angle - weight, - italic, - FALSE, // underline attribute flag - FALSE, // strikeout attribute flag - DEFAULT_CHARSET, // character set identifier - OUT_DEFAULT_PRECIS, // output precision - CLIP_DEFAULT_PRECIS,// clipping precision - DEFAULT_QUALITY, // output quality - DEFAULT_PITCH, // pitch and family - (LPCWSTR)wstr // pointer to typeface name string - ); - angle = fl_angle_; - HDC gc = (HDC)fl_graphics_driver->gc(); - if (!gc) gc = fl_GetDC(0); - SelectObject(gc, fid); - GetTextMetrics(gc, &metr); -// BOOL ret = GetCharWidthFloat(fl_gc, metr.tmFirstChar, metr.tmLastChar, font->width+metr.tmFirstChar); -// ...would be the right call, but is not implemented into Window95! (WinNT?) - //GetCharWidth(fl_gc, 0, 255, width); - int i; - memset(width, 0, 64 * sizeof(int*)); -#if HAVE_GL - for (i = 0; i < 64; i++) glok[i] = 0; -#endif - size = fsize; -} - -Fl_GDI_Font_Descriptor::~Fl_GDI_Font_Descriptor() { -#if HAVE_GL -// Delete list created by gl_draw(). This is not done by this code -// as it will link in GL unnecessarily. There should be some kind -// of "free" routine pointer, or a subclass? -#endif - if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL); - DeleteObject(fid); - for (int i = 0; i < 64; i++) { - if ( width[i] ) free(width[i]); - } -} - -//////////////////////////////////////////////////////////////// - -// WARNING: if you add to this table, you must redefine FL_FREE_FONT -// in Enumerations.H & recompile!! -static Fl_Fontdesc built_in_table[] = { - {" Microsoft Sans Serif"}, - {"BMicrosoft Sans Serif"}, - {"IMicrosoft Sans Serif"}, - {"PMicrosoft Sans Serif"}, -{" Courier New"}, -{"BCourier New"}, -{"ICourier New"}, -{"PCourier New"}, -{" Times New Roman"}, -{"BTimes New Roman"}, -{"ITimes New Roman"}, -{"PTimes New Roman"}, -{" Symbol"}, -{" Terminal"}, -{"BTerminal"}, -{" Wingdings"}, -}; - -Fl_Fontdesc* fl_fonts = built_in_table; - -static Fl_GDI_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size, int angle) { - Fl_Fontdesc* s = fl_fonts+fnum; - if (!s->name) s = fl_fonts; // use 0 if fnum undefined - Fl_GDI_Font_Descriptor* f; - for (f = (Fl_GDI_Font_Descriptor*)s->first; f; f = (Fl_GDI_Font_Descriptor*)f->next) - if (f->size == size && f->angle == angle) return f; - f = new Fl_GDI_Font_Descriptor(s->name, size); - f->next = s->first; - s->first = f; - return f; -} - -//////////////////////////////////////////////////////////////// -// Public interface: - -static void fl_font(Fl_Graphics_Driver *driver, Fl_Font fnum, Fl_Fontsize size, int angle) { - if (fnum==-1) { // just make sure that we will load a new font next time - fl_angle_ = 0; - driver->Fl_Graphics_Driver::font(0, 0); - return; - } - if (fnum == driver->Fl_Graphics_Driver::font() && size == ((Fl_GDI_Graphics_Driver*)driver)->size_unscaled() && angle == fl_angle_) return; - fl_angle_ = angle; - driver->Fl_Graphics_Driver::font(fnum, size); - driver->font_descriptor( find(fnum, size, angle) ); -} - -void Fl_GDI_Graphics_Driver::font_unscaled(Fl_Font fnum, Fl_Fontsize size) { - fl_font(this, fnum, size, 0); -} - -int Fl_GDI_Graphics_Driver::height_unscaled() { - Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor(); - if (fl_fontsize) return (fl_fontsize->metr.tmAscent + fl_fontsize->metr.tmDescent); - else return -1; -} - -int Fl_GDI_Graphics_Driver::descent_unscaled() { - Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor(); - if (fl_fontsize) return fl_fontsize->metr.tmDescent; - else return -1; -} - -Fl_Fontsize Fl_GDI_Graphics_Driver::size_unscaled() { - if (font_descriptor()) return size_; - return -1; -} - -double Fl_GDI_Graphics_Driver::width_unscaled(const char* c, int n) { - if (n == 0) return 0; - int len1 = fl_utf8len1(*c); - if (n > len1 && len1 > 0) { // a text with several codepoints: compute its typographical width - int wn = fl_utf8toUtf16(c, n, wstr, wstr_len); - if (wn >= wstr_len) { - wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1)); - wstr_len = wn + 1; - wn = fl_utf8toUtf16(c, n, wstr, wstr_len); - } - HDC gc2 = gc_; - HWND hWnd; - if (!gc2) { - hWnd = Fl::first_window() ? fl_xid(Fl::first_window()) : NULL; - gc2 = GetDC(hWnd); - } - SelectObject(gc2, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid); - SIZE s; - GetTextExtentPoint32W(gc2, (WCHAR*)wstr, wn, &s); - if (gc2 && gc2 != gc_) ReleaseDC(hWnd, gc2); - return (double)s.cx; - } - int i = 0; - if (!font_descriptor()) return -1.0; - double w = 0.0; - char *end = (char *)&c[n]; - while (i < n) { - unsigned int ucs; - int l; - ucs = fl_utf8decode((const char*)(c + i), end, &l); -// if (l < 1) l = 1; - i += l; - if (!fl_nonspacing(ucs)) { - w += width_unscaled(ucs); - } - } - return w; -} - -double Fl_GDI_Graphics_Driver::width_unscaled(unsigned int c) { - Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor(); - unsigned int r; - SIZE s; - // Special Case Handling of Unicode points over U+FFFF. - // The logic (below) computes a lookup table for char widths - // on-the-fly, but the table only covers codepoints up to - // U+FFFF, which covers the basic multilingual plane, but - // not any higher plane, or glyphs that require surrogate-pairs - // to encode them in WinXX, which is UTF16. - // This code assumes that these glyphs are rarely used and simply - // measures them explicitly if they occur - This will be slow... - if(c > 0x0000FFFF) { // UTF16 surrogate pair is needed - if (!gc_) { // We have no valid gc, so nothing to measure - bail out - return 0.0; - } - int cc; // cell count - unsigned short u16[4]; // Array for UTF16 representation of c - // Creates a UTF16 string from a UCS code point. - cc = fl_ucs_to_Utf16(c, u16, 4); - // Make sure the current font is selected before we make the measurement - SelectObject(gc_, fl_fontsize->fid); - // measure the glyph width - GetTextExtentPoint32W(gc_, (WCHAR*)u16, cc, &s); - return (double)s.cx; - } - // else - this falls through to the lookup-table for glyph widths - // in the basic multilingual plane - r = (c & 0xFC00) >> 10; - if (!fl_fontsize->width[r]) { - fl_fontsize->width[r] = (int*) malloc(sizeof(int) * 0x0400); - for (int i = 0; i < 0x0400; i++) fl_fontsize->width[r][i] = -1; - } else { - if ( fl_fontsize->width[r][c&0x03FF] >= 0 ) { // already cached - return (double) fl_fontsize->width[r][c & 0x03FF]; - } - } - unsigned short ii = r * 0x400; - // The following code makes a best effort attempt to obtain a valid fl_gc. - // If no fl_gc is available at the time we call fl_width(), then we first - // try to obtain a gc from the first fltk window. - // If that is null then we attempt to obtain the gc from the current screen - // using (GetDC(NULL)). - // This should resolve STR #2086 - HDC gc2 = gc_; - HWND hWnd = 0; - if (!gc2) { // We have no valid gc, try and obtain one - // Use our first fltk window, or fallback to using the screen via GetDC(NULL) - hWnd = Fl::first_window() ? fl_xid(Fl::first_window()) : NULL; - gc2 = GetDC(hWnd); - } - if (!gc2) Fl::fatal("Invalid graphic context: fl_width() failed because no valid HDC was found!"); - SelectObject(gc2, fl_fontsize->fid); - ii += c &0x03FF; - GetTextExtentPoint32W(gc2, (WCHAR*)&ii, 1, &s); - fl_fontsize->width[r][c&0x03FF] = s.cx; - if (gc2 && gc2 != gc_) ReleaseDC(hWnd, gc2); - return (double) fl_fontsize->width[r][c & 0x03FF]; -} - -/* Add function pointer to allow us to access GetGlyphIndicesW on systems that have it, - * without crashing on systems that do not. */ -/* DWORD WINAPI GetGlyphIndicesW(HDC,LPCWSTR,int,LPWORD,DWORD) */ -typedef DWORD (WINAPI* fl_GetGlyphIndices_func)(HDC,LPCWSTR,int,LPWORD,DWORD); - -static fl_GetGlyphIndices_func fl_GetGlyphIndices = NULL; // used to hold a proc pointer for GetGlyphIndicesW -static int have_loaded_GetGlyphIndices = 0; // Set this non-zero once we have tried to load GetGlyphIndices - -// Function that tries to dynamically load GetGlyphIndicesW at runtime -static void GetGlyphIndices_init() { - // Since not all versions of Windows include GetGlyphIndicesW support, - // we do a run-time check for the required function. - HMODULE hMod = GetModuleHandle("GDI32.DLL"); - if (hMod) { - // check that GetGlyphIndicesW is available - fl_GetGlyphIndices = (fl_GetGlyphIndices_func)GetProcAddress(hMod, "GetGlyphIndicesW"); - } - have_loaded_GetGlyphIndices = -1; // set this non-zero when we have attempted to load GetGlyphIndicesW -} // GetGlyphIndices_init function - -static void on_printer_extents_update(int &dx, int &dy, int &w, int &h, HDC gc) -// converts text extents from device coords to logical coords -{ - POINT pt[3] = { {0, 0}, {dx, dy}, {dx+w, dy+h} }; - DPtoLP(gc, pt, 3); - w = pt[2].x - pt[1].x; - h = pt[2].y - pt[1].y; - dx = pt[1].x - pt[0].x; - dy = pt[1].y - pt[0].y; -} - -// if printer context, extents shd be converted to logical coords -#define EXTENTS_UPDATE(x,y,w,h,gc) \ - if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) { \ - on_printer_extents_update(x,y,w,h,gc); \ - } - -// Function to determine the extent of the "inked" area of the glyphs in a string -void Fl_GDI_Graphics_Driver::text_extents_unscaled(const char *c, int n, int &dx, int &dy, int &w, int &h) { - - Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor(); - if (!fl_fontsize) { // no valid font, nothing to measure - w = 0; h = 0; - dx = dy = 0; - return; - } - - static unsigned short *ext_buff = NULL; // UTF-16 converted version of input UTF-8 string - static WORD *w_buff = NULL; // glyph indices array - static unsigned wc_len = 0; // current string buffer dimensions - static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; // identity mat for GetGlyphOutlineW - GLYPHMETRICS metrics; - int maxw = 0, maxh = 0, dh; - int minx = 0, miny = -999999; - unsigned len = 0, idx = 0; - HWND hWnd = 0; - HDC gc2 = gc_; // local copy of current gc - make a copy in case we change it... - int has_surrogates; // will be set if the string contains surrogate pairs - - // Have we loaded the GetGlyphIndicesW function yet? - if (have_loaded_GetGlyphIndices == 0) { - GetGlyphIndices_init(); - } - // Do we have a usable GetGlyphIndices function? - if(!fl_GetGlyphIndices) goto exit_error; // No GetGlyphIndices function, use fallback mechanism instead - - // The following code makes a best effort attempt to obtain a valid fl_gc. - // See description in fl_width() above for an explanation. - if (!gc2) { // We have no valid gc, try and obtain one - // Use our first fltk window, or fallback to using the screen via GetDC(NULL) - hWnd = Fl::first_window() ? fl_xid(Fl::first_window()) : NULL; - gc2 = GetDC(hWnd); - } - if (!gc2) goto exit_error; // no valid gc, attempt to use fallback measure - - // now convert the string to WCHAR and measure it - len = fl_utf8toUtf16(c, n, ext_buff, wc_len); - if(len >= wc_len) { - if(ext_buff) {delete [] ext_buff;} - if(w_buff) {delete [] w_buff;} - wc_len = len + 64; - ext_buff = new unsigned short[wc_len]; - w_buff = new WORD[wc_len]; - len = fl_utf8toUtf16(c, n, ext_buff, wc_len); - } - SelectObject(gc2, fl_fontsize->fid); - - // Are there surrogate-pairs in this string? If so GetGlyphIndicesW will fail - // since it can only handle the BMP range. - // We ideally want to use GetGlyphIndicesW, as it is the Right Thing, but it - // only works for the BMP, so we leverage GetCharacterPlacementW instead, which - // is not ideal, but works adequately well, and does handle surrogate pairs. - has_surrogates = 0; - for(unsigned ll = 0; ll < len; ll++) { - if((ext_buff[ll] >= 0xD800) && (ext_buff[ll] < 0xE000)) { - has_surrogates = -1; - break; - } - } - if (has_surrogates) { - // GetGlyphIndices will not work - use GetCharacterPlacementW() instead - GCP_RESULTSW gcp_res; - memset(w_buff, 0, (sizeof(WORD) * wc_len)); - memset(&gcp_res, 0, sizeof(GCP_RESULTSW)); - gcp_res.lpGlyphs = (LPWSTR)w_buff; - gcp_res.nGlyphs = wc_len; - gcp_res.lStructSize = sizeof(gcp_res); - - DWORD dr = GetCharacterPlacementW(gc2, (WCHAR*)ext_buff, len, 0, &gcp_res, GCP_GLYPHSHAPE); - if(dr) { - len = gcp_res.nGlyphs; - } else goto exit_error; - } else { - if (fl_GetGlyphIndices(gc_, (WCHAR*)ext_buff, len, w_buff, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) { - // some error occurred here - just return fl_measure values - goto exit_error; - } - } - - // now we have the glyph array we measure each glyph in turn... - for(idx = 0; idx < len; idx++){ - if (GetGlyphOutlineW (gc2, w_buff[idx], GGO_METRICS | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix) == GDI_ERROR) { - goto exit_error; - } - maxw += metrics.gmCellIncX; - if(idx == 0) minx = metrics.gmptGlyphOrigin.x; - dh = metrics.gmBlackBoxY - metrics.gmptGlyphOrigin.y; - if(dh > maxh) maxh = dh; - if(miny < metrics.gmptGlyphOrigin.y) miny = metrics.gmptGlyphOrigin.y; - } - // for the last cell, we only want the bounding X-extent, not the glyphs increment step - maxw = maxw - metrics.gmCellIncX + metrics.gmBlackBoxX + metrics.gmptGlyphOrigin.x; - w = maxw - minx; - h = maxh + miny; - dx = minx; - dy = -miny; - EXTENTS_UPDATE(dx, dy, w, h, gc_); - return; // normal exit - -exit_error: - // some error here - just return fl_measure values - w = (int)width(c, n); - h = height_unscaled(); - dx = 0; - dy = descent_unscaled() - h; - EXTENTS_UPDATE(dx, dy, w, h, gc_); - return; -} // fl_text_extents - -void Fl_GDI_Graphics_Driver::draw_unscaled(const char* str, int n, int x, int y) { - COLORREF oldColor = SetTextColor(gc_, fl_RGB()); - // avoid crash if no font has been set yet - if (!font_descriptor()) this->font(FL_HELVETICA, FL_NORMAL_SIZE); - SelectObject(gc_, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid); - int wn = fl_utf8toUtf16(str, n, wstr, wstr_len); - if(wn >= wstr_len) { - wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1)); - wstr_len = wn + 1; - wn = fl_utf8toUtf16(str, n, wstr, wstr_len); - } - TextOutW(gc_, x, y, (WCHAR*)wstr, wn); - SetTextColor(gc_, oldColor); // restore initial state -} - -void Fl_GDI_Graphics_Driver::draw_unscaled(int angle, const char* str, int n, int x, int y) { - fl_font(this, Fl_Graphics_Driver::font(), size_unscaled(), angle); - int wn = 0; // count of UTF16 cells to render full string - COLORREF oldColor = SetTextColor(gc_, fl_RGB()); - SelectObject(gc_, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid); - wn = fl_utf8toUtf16(str, n, wstr, wstr_len); - if(wn >= wstr_len) { // Array too small - wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1)); - wstr_len = wn + 1; - wn = fl_utf8toUtf16(str, n, wstr, wstr_len); // respin the translation - } - TextOutW(gc_, x, y, (WCHAR*)wstr, wn); - SetTextColor(gc_, oldColor); - fl_font(this, Fl_Graphics_Driver::font(), size_unscaled(), 0); -} - -void Fl_GDI_Graphics_Driver::rtl_draw_unscaled(const char* c, int n, int x, int y) { - int wn; - wn = fl_utf8toUtf16(c, n, wstr, wstr_len); - if(wn >= wstr_len) { - wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1)); - wstr_len = wn + 1; - wn = fl_utf8toUtf16(c, n, wstr, wstr_len); - } - - COLORREF oldColor = SetTextColor(gc_, fl_RGB()); - SelectObject(gc_, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid); -#ifdef RTL_CHAR_BY_CHAR - int i = 0; - int lx = 0; - while (i < wn) { // output char by char is very bad for Arabic but coherent with fl_width() - lx = (int) width(wstr[i]); - x -= lx; - TextOutW(gc_, x, y, (WCHAR*)wstr + i, 1); - if (fl_nonspacing(wstr[i])) { - x += lx; - } - i++; - } -#else - UINT old_align = SetTextAlign(gc_, TA_RIGHT | TA_RTLREADING); - TextOutW(gc_, x, y - height_unscaled() + descent_unscaled(), (WCHAR*)wstr, wn); - SetTextAlign(gc_, old_align); -#endif - SetTextColor(gc_, oldColor); -} -#endif diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_image.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_image.cxx deleted file mode 100644 index 3a8e70689..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_image.cxx +++ /dev/null @@ -1,826 +0,0 @@ -// -// Windows image drawing code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2024 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// I hope a simple and portable method of drawing color and monochrome -// images. To keep this simple, only a single storage type is -// supported: 8 bit unsigned data, byte order RGB, and pixels are -// stored packed into rows with the origin at the top-left. It is -// possible to alter the size of pixels with the "delta" argument, to -// add alpha or other information per pixel. It is also possible to -// change the origin and direction of the image data by messing with -// the "delta" and "linedelta", making them negative, though this may -// defeat some of the shortcuts in translating the image for X. - -// Unbelievably (since it conflicts with how most PC software works) -// Micro$oft picked a bottom-up and BGR storage format for their -// DIB images. I'm pretty certain there is a way around this, but -// I can't find any other than the brute-force method of drawing -// each line as a separate image. This may also need to be done -// if the delta is any amount other than 1, 3, or 4. - -//////////////////////////////////////////////////////////////// - -#include -#include "Fl_GDI_Graphics_Driver.H" -#include "../WinAPI/Fl_WinAPI_System_Driver.H" -#include -#include -#include -#include - -#define MAXBUFFER 0x40000 // 256k - -void fl_release_dc(HWND, HDC); // from Fl_win32.cxx - -#if USE_COLORMAP - -// error-diffusion dither into the FLTK colormap -static void dither(uchar* to, const uchar* from, int w, int delta) { - static int ri, gi, bi, dir; - int r=ri, g=gi, b=bi; - int d, td; - if (dir) { - dir = 0; - from = from+(w-1)*delta; - to = to+(w-1); - d = -delta; - td = -1; - } else { - dir = 1; - d = delta; - td = 1; - } - for (; w--; from += d, to += td) { - r += from[0]; if (r < 0) r = 0; else if (r>255) r = 255; - int rr = r*FL_NUM_RED/256; - r -= rr*255/(FL_NUM_RED-1); - g += from[1]; if (g < 0) g = 0; else if (g>255) g = 255; - int gg = g*FL_NUM_GREEN/256; - g -= gg*255/(FL_NUM_GREEN-1); - b += from[2]; if (b < 0) b = 0; else if (b>255) b = 255; - int bb = b*FL_NUM_BLUE/256; - b -= bb*255/(FL_NUM_BLUE-1); - *to = uchar(FL_COLOR_CUBE+(bb*FL_NUM_RED+rr)*FL_NUM_GREEN+gg); - } - ri = r; gi = g; bi = b; -} - -// error-diffusion dither into the FLTK colormap -static void monodither(uchar* to, const uchar* from, int w, int delta) { - static int ri,dir; - int r=ri; - int d, td; - if (dir) { - dir = 0; - from = from+(w-1)*delta; - to = to+(w-1); - d = -delta; - td = -1; - } else { - dir = 1; - d = delta; - td = 1; - } - for (; w--; from += d, to += td) { - r += *from; if (r < 0) r = 0; else if (r>255) r = 255; - int rr = r*FL_NUM_GRAY/256; - r -= rr*255/(FL_NUM_GRAY-1); - *to = uchar(FL_GRAY_RAMP+rr); - } - ri = r; -} - -#endif // USE_COLORMAP - -static int fl_abs(int v) { return v<0 ? -v : v; } - -static void innards(const uchar *buf, int X, int Y, int W, int H, - int delta, int linedelta, int depth, - Fl_Draw_Image_Cb cb, void* userdata, HDC gc) -{ - char indexed = 0; - -#if USE_COLORMAP - indexed = (fl_palette != 0); -#endif - - if (depth==0) depth = 3; - if (indexed || !fl_can_do_alpha_blending()) - depth = (depth-1)|1; - - if (!linedelta) linedelta = W*fl_abs(delta); - - int x = 0, y = 0, w = 0, h = 0; - fl_clip_box(X, Y, W, H, x, y, w, h); - if (w<=0 || h<=0) return; - if (buf) buf += (x-X)*delta + (y-Y)*linedelta; - - // bmibuffer: BITMAPINFOHEADER + 256 colors (RGBQUAD) + 1 (rounding effects ?) - static U32 bmibuffer[sizeof(BITMAPINFOHEADER)/4 + 257]; - BITMAPINFO *bmi = (BITMAPINFO*)bmibuffer; - if (!bmi->bmiHeader.biSize) { - bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi->bmiHeader.biPlanes = 1; - bmi->bmiHeader.biCompression = BI_RGB; - bmi->bmiHeader.biXPelsPerMeter = 0; - bmi->bmiHeader.biYPelsPerMeter = 0; - bmi->bmiHeader.biClrUsed = 0; - bmi->bmiHeader.biClrImportant = 0; - } -#if USE_COLORMAP - if (indexed) { - for (short i=0; i<256; i++) { - *((short*)(bmi->bmiColors)+i) = i; - } - } else -#endif - if (depth<3) { - RGBQUAD *bmi_colors = &(bmi->bmiColors[0]); // use pointer to suppress warning (STR #3199) - for (int i=0; i<256; i++) { - bmi_colors[i].rgbBlue = (uchar)i; // = bmi->bmiColors[i] - bmi_colors[i].rgbGreen = (uchar)i; - bmi_colors[i].rgbRed = (uchar)i; - bmi_colors[i].rgbReserved = (uchar)0; // must be zero - } - } - bmi->bmiHeader.biWidth = w; -#if USE_COLORMAP - bmi->bmiHeader.biBitCount = indexed ? 8 : depth*8; - int pixelsize = indexed ? 1 : depth; -#else - bmi->bmiHeader.biBitCount = depth*8; - int pixelsize = depth; -#endif - if (depth==2) { // special case: gray with alpha - bmi->bmiHeader.biBitCount = 32; - pixelsize = 4; - } - int linesize = (pixelsize*w+3)&~3; - - static U32* buffer; - static long buffer_size; - int blocking = h; - { - int size = linesize * h; - // when printing, don't limit buffer size not to get a crash in StretchDIBits - if (size > MAXBUFFER && !fl_graphics_driver->has_feature(Fl_Graphics_Driver::PRINTER)) { - size = MAXBUFFER; - blocking = MAXBUFFER / linesize; - } - if (size > buffer_size) { - delete[] buffer; - buffer_size = size; - buffer = new U32[(size + 3) / 4]; - } - } - bmi->bmiHeader.biHeight = blocking; - static U32* line_buffer; - if (!buf) { - int size = W*delta; - static int line_buf_size; - if (size > line_buf_size) { - delete[] line_buffer; - line_buf_size = size; - line_buffer = new U32[(size+3)/4]; - } - } - for (int j=0; j>8; - to[0] = gray; - to[1] = gray; - to[2] = gray; - to[3] = a; - } - break; - case 3: - for (i=w; i--; from += delta, to += 3) { - uchar r = from[0]; - to[0] = from[2]; - to[1] = from[1]; - to[2] = r; - } - break; - case 4: - for (i=w; i--; from += delta, to += 4) { - uchar a = from[3]; - uchar r = from[0]; - to[0] = (from[2]*a)>>8; - to[1] = (from[1]*a)>>8; - to[2] = (r*a)>>8; - to[3] = from[3]; - } - break; - } - } - } // for (k = 0; jhas_feature(Fl_Graphics_Driver::PRINTER)) { - // if print context, device and logical units are not equal, so SetDIBitsToDevice - // does not do the expected job, whereas StretchDIBits does it. - StretchDIBits(gc, x, y+j-k, w, k, 0, 0, w, k, - (LPSTR)((uchar*)buffer+(blocking-k)*linesize), - bmi, -#if USE_COLORMAP - indexed ? DIB_PAL_COLORS : DIB_RGB_COLORS -#else - DIB_RGB_COLORS -#endif - , SRCCOPY ); - delete[] buffer; - buffer = NULL; - buffer_size = 0; - } - else { - SetDIBitsToDevice(gc, x, y+j-k, w, k, 0, 0, 0, k, - (LPSTR)((uchar*)buffer+(blocking-k)*linesize), - bmi, -#if USE_COLORMAP - indexed ? DIB_PAL_COLORS : DIB_RGB_COLORS -#else - DIB_RGB_COLORS -#endif - ); - } - } // for (int j=0; j-3),0,0, gc_); - } -} - -void Fl_GDI_Graphics_Driver::draw_image_unscaled(Fl_Draw_Image_Cb cb, void* data, - int x, int y, int w, int h,int d) { - if (fl_abs(d)&FL_IMAGE_WITH_ALPHA) { - d ^= FL_IMAGE_WITH_ALPHA; - innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data, gc_); - } else { - innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data, gc_); - } -} - -void Fl_GDI_Graphics_Driver::draw_image_mono_unscaled(const uchar* buf, int x, int y, int w, int h, int d, int l){ - if (fl_abs(d)&FL_IMAGE_WITH_ALPHA) { - d ^= FL_IMAGE_WITH_ALPHA; - innards(buf,x,y,w,h,d,l,1,0,0, gc_); - } else { - innards(buf,x,y,w,h,d,l,1,0,0, gc_); - } -} - -void Fl_GDI_Graphics_Driver::draw_image_mono_unscaled(Fl_Draw_Image_Cb cb, void* data, - int x, int y, int w, int h,int d) { - if (fl_abs(d)&FL_IMAGE_WITH_ALPHA) { - d ^= FL_IMAGE_WITH_ALPHA; - innards(0,x,y,w,h,d,0,1,cb,data, gc_); - } else { - innards(0,x,y,w,h,d,0,1,cb,data, gc_); - } -} - -#if USE_COLORMAP -void Fl_GDI_Graphics_Driver::colored_rectf(int x, int y, int w, int h, uchar r, uchar g, uchar b) { - // use the error diffusion dithering code to produce a much nicer block: - if (fl_palette) { - uchar c[3]; - c[0] = r; c[1] = g; c[2] = b; - innards(c, floor(x), floor(y), floor(x + w) - floor(x), floor(y + h) - floor(y), - 0,0,0,0,0, (HDC)gc()); - return; - } - Fl_Graphics_Driver::colored_rectf(x, y, w, h, r, g, b); -} -#endif - -// Create an N-bit bitmap for masking... -HBITMAP Fl_GDI_Graphics_Driver::create_bitmask(int w, int h, const uchar *data) { - // this won't work when the user changes display mode during run or - // has two screens with different depths - HBITMAP bm; - static uchar hiNibble[16] = - { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0 }; - static uchar loNibble[16] = - { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, - 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f }; - HDC current_gc = (HDC)Fl_Surface_Device::surface()->driver()->gc(); - int np = GetDeviceCaps(current_gc, PLANES); //: was always one on sample machines - int bpp = GetDeviceCaps(current_gc, BITSPIXEL);//: 1,4,8,16,24,32 and more odd stuff? - int Bpr = (bpp*w+7)/8; //: bytes per row - int pad = Bpr&1, w1 = (w+7)/8, shr = ((w-1)&7)+1; - if (bpp==4) shr = (shr+1)/2; - uchar *newarray = new uchar[(Bpr+pad)*h]; - uchar *dst = newarray; - const uchar *src = data; - - for (int i=0; i0; j--) { - uchar b = *src++; - if (bpp==1) { - *dst++ = (uchar)( hiNibble[b&15] ) | ( loNibble[(b>>4)&15] ); - } else if (bpp==4) { - for (int k=(j==1)?shr:4; k>0; k--) { - *dst++ = (uchar)("\377\360\017\000"[b&3]); - b = b >> 2; - } - } else { - for (int k=(j==1)?shr:8; k>0; k--) { - if (b&1) { - *dst++=0; - if (bpp>8) *dst++=0; - if (bpp>16) *dst++=0; - if (bpp>24) *dst++=0; - } else { - *dst++=0xff; - if (bpp>8) *dst++=0xff; - if (bpp>16) *dst++=0xff; - if (bpp>24) *dst++=0xff; - } - - b = b >> 1; - } - } - } - - dst += pad; - } - - bm = CreateBitmap(w, h, np, bpp, newarray); - delete[] newarray; - - return bm; -} - -void Fl_GDI_Graphics_Driver::delete_bitmask(fl_uintptr_t bm) { - DeleteObject((HGDIOBJ)bm); -} - -void Fl_GDI_Graphics_Driver::draw_fixed(Fl_Bitmap *bm, int X, int Y, int W, int H, int cx, int cy) { - X = this->floor(X); - Y = this->floor(Y); - cache_size(bm, W, H); - cx = this->floor(cx); cy = this->floor(cy); - - HDC tempdc = CreateCompatibleDC(gc_); - int save = SaveDC(tempdc); - SelectObject(tempdc, (HGDIOBJ)*Fl_Graphics_Driver::id(bm)); - SelectObject(gc_, fl_brush()); - // secret bitblt code found in old Windows reference manual: - BitBlt(gc_, X, Y, W, H, tempdc, cx, cy, 0xE20746L); - RestoreDC(tempdc, save); - DeleteDC(tempdc); -} - -Fl_GDI_Printer_Graphics_Driver::transparent_f_type Fl_GDI_Printer_Graphics_Driver::TransparentBlt() { - HMODULE hMod; - static transparent_f_type fpter = ( (hMod = LoadLibrary("MSIMG32.DLL")) ? - (transparent_f_type)GetProcAddress(hMod, "TransparentBlt") : NULL - ); - return fpter; -} - -void Fl_GDI_Printer_Graphics_Driver::draw_bitmap(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy) { - int X, Y, W, H; - if (Fl_Graphics_Driver::start_image(bm, XP, YP, WP, HP, cx, cy, X, Y, W, H)) { - return; - } - transparent_f_type fl_TransparentBlt = TransparentBlt(); - if (!fl_TransparentBlt) { - Fl_Graphics_Driver::draw_bitmap(bm, X, Y, W, H, cx, cy); - return; - } - bool recache = false; - if (*id(bm)) { - int *pw, *ph; - cache_w_h(bm, pw, ph); - recache = (*pw != bm->data_w() || *ph != bm->data_h()); - } - if (recache || !*id(bm)) { - bm->uncache(); - cache(bm); - } - HDC tempdc; - int save; - // algorithm for bitmap output to Fl_GDI_Printer - Fl_Color save_c = fl_color(); // save bitmap's desired color - uchar r, g, b; - Fl::get_color(save_c, r, g, b); - r = 255-r; - g = 255-g; - b = 255-b; - Fl_Color background = fl_rgb_color(r, g, b); // a color very different from the bitmap's - Fl_Image_Surface *img_surf = new Fl_Image_Surface(bm->data_w(), bm->data_h()); - Fl_Surface_Device::push_current(img_surf); - fl_color(background); - fl_rectf(0,0, bm->data_w(), bm->data_h()); // use this color as offscreen background - fl_color(save_c); // back to bitmap's color - HDC off_gc = (HDC)fl_graphics_driver->gc(); - tempdc = CreateCompatibleDC(off_gc); - save = SaveDC(tempdc); - SelectObject(tempdc, (HGDIOBJ)*Fl_Graphics_Driver::id(bm)); - SelectObject(off_gc, fl_brush()); // use bitmap's desired color - BitBlt(off_gc, 0, 0, bm->data_w(), bm->data_h(), tempdc, 0, 0, 0xE20746L); // draw bitmap to offscreen - Fl_Surface_Device::pop_current(); - SelectObject(tempdc, (HGDIOBJ)img_surf->offscreen()); // use offscreen data - // draw it to printer context with background color as transparent - float scaleW = bm->data_w()/float(bm->w()); - float scaleH = bm->data_h()/float(bm->h()); - fl_TransparentBlt(gc_, X, Y, W, H, tempdc, - int(cx * scaleW), int(cy * scaleH), int(W * scaleW), int(H * scaleH), RGB(r, g, b) ); - delete img_surf; - RestoreDC(tempdc, save); - DeleteDC(tempdc); - if (recache) bm->uncache(); -} - - -// Create a 1-bit mask used for alpha blending -HBITMAP Fl_GDI_Graphics_Driver::create_alphamask(int w, int h, int d, int ld, const uchar *array) { - HBITMAP bm; - int bmw = (w + 7) / 8; - uchar *bitmap = new uchar[bmw * h]; - uchar *bitptr, bit; - const uchar *dataptr; - int x, y; - static uchar dither[16][16] = { // Simple 16x16 Floyd dither - { 0, 128, 32, 160, 8, 136, 40, 168, - 2, 130, 34, 162, 10, 138, 42, 170 }, - { 192, 64, 224, 96, 200, 72, 232, 104, - 194, 66, 226, 98, 202, 74, 234, 106 }, - { 48, 176, 16, 144, 56, 184, 24, 152, - 50, 178, 18, 146, 58, 186, 26, 154 }, - { 240, 112, 208, 80, 248, 120, 216, 88, - 242, 114, 210, 82, 250, 122, 218, 90 }, - { 12, 140, 44, 172, 4, 132, 36, 164, - 14, 142, 46, 174, 6, 134, 38, 166 }, - { 204, 76, 236, 108, 196, 68, 228, 100, - 206, 78, 238, 110, 198, 70, 230, 102 }, - { 60, 188, 28, 156, 52, 180, 20, 148, - 62, 190, 30, 158, 54, 182, 22, 150 }, - { 252, 124, 220, 92, 244, 116, 212, 84, - 254, 126, 222, 94, 246, 118, 214, 86 }, - { 3, 131, 35, 163, 11, 139, 43, 171, - 1, 129, 33, 161, 9, 137, 41, 169 }, - { 195, 67, 227, 99, 203, 75, 235, 107, - 193, 65, 225, 97, 201, 73, 233, 105 }, - { 51, 179, 19, 147, 59, 187, 27, 155, - 49, 177, 17, 145, 57, 185, 25, 153 }, - { 243, 115, 211, 83, 251, 123, 219, 91, - 241, 113, 209, 81, 249, 121, 217, 89 }, - { 15, 143, 47, 175, 7, 135, 39, 167, - 13, 141, 45, 173, 5, 133, 37, 165 }, - { 207, 79, 239, 111, 199, 71, 231, 103, - 205, 77, 237, 109, 197, 69, 229, 101 }, - { 63, 191, 31, 159, 55, 183, 23, 151, - 61, 189, 29, 157, 53, 181, 21, 149 }, - { 254, 127, 223, 95, 247, 119, 215, 87, - 253, 125, 221, 93, 245, 117, 213, 85 } - }; - - // Generate a 1-bit "screen door" alpha mask; not always pretty, but - // definitely fast... In the future we may be able to support things - // like the RENDER extension in XFree86, when available, to provide - // true RGBA-blended rendering. See: - // - // http://www.xfree86.org/~keithp/render/protocol.html - // - // for more info on XRender... - // - memset(bitmap, 0, bmw * h); - - for (dataptr = array + d - 1, y = 0; y < h; y ++, dataptr += ld) - for (bitptr = bitmap + y * bmw, bit = 1, x = 0; x < w; x ++, dataptr += d) { - if (*dataptr > dither[x & 15][y & 15]) - *bitptr |= bit; - if (bit < 128) bit <<= 1; - else { - bit = 1; - bitptr ++; - } - } - - bm = create_bitmask(w, h, bitmap); - delete[] bitmap; - - return bm; -} - - -void Fl_GDI_Graphics_Driver::cache(Fl_RGB_Image *img) -{ - Fl_Image_Surface *surface = new Fl_Image_Surface(img->data_w(), img->data_h()); - Fl_Surface_Device::push_current(surface); - if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) { - fl_draw_image(img->array, 0, 0, img->data_w(), img->data_h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld()); - } else { - fl_draw_image(img->array, 0, 0, img->data_w(), img->data_h(), img->d(), img->ld()); - if (img->d() == 2 || img->d() == 4) { - *Fl_Graphics_Driver::mask(img) = (fl_uintptr_t)create_alphamask(img->data_w(), img->data_h(), img->d(), img->ld(), img->array); - } - } - Fl_Surface_Device::pop_current(); - Fl_Offscreen offs = Fl_Graphics_Driver::get_offscreen_and_delete_image_surface(surface); - int *pw, *ph; - cache_w_h(img, pw, ph); - *pw = img->data_w(); - *ph = img->data_h(); - *Fl_Graphics_Driver::id(img) = (fl_uintptr_t)offs; -} - - -void Fl_GDI_Graphics_Driver::draw_fixed(Fl_RGB_Image *img, int X, int Y, int W, int H, int cx, int cy) { - X = this->floor(X); - Y = this->floor(Y); - cache_size(img, W, H); - cx = this->floor(cx); cy = this->floor(cy); - if (W + cx > img->data_w()) W = img->data_w() - cx; - if (H + cy > img->data_h()) H = img->data_h() - cy; - if (!*Fl_Graphics_Driver::id(img)) { - cache(img); - } - if (*Fl_Graphics_Driver::mask(img)) { - HDC new_gc = CreateCompatibleDC(gc_); - int save = SaveDC(new_gc); - SelectObject(new_gc, (void*)*Fl_Graphics_Driver::mask(img)); - BitBlt(gc_, X, Y, W, H, new_gc, cx, cy, SRCAND); - SelectObject(new_gc, (void*)*Fl_Graphics_Driver::id(img)); - BitBlt(gc_, X, Y, W, H, new_gc, cx, cy, SRCPAINT); - RestoreDC(new_gc,save); - DeleteDC(new_gc); - } else if (img->d()==2 || img->d()==4) { - copy_offscreen_with_alpha(X, Y, W, H, (HBITMAP)*Fl_Graphics_Driver::id(img), cx, cy); - } else { - copy_offscreen(X, Y, W, H, (Fl_Offscreen)*Fl_Graphics_Driver::id(img), cx, cy); - } -} - - -void Fl_GDI_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) { - if (Fl_Graphics_Driver::start_image(rgb, XP, YP, WP, HP, cx, cy, XP, YP, WP, HP)) { - return; - } - if ((rgb->d() % 2) == 0 && !fl_can_do_alpha_blending()) { - Fl_Graphics_Driver::draw_rgb(rgb, XP, YP, WP, HP, cx, cy); - return; - } - if (!*Fl_Graphics_Driver::id(rgb)) { - cache(rgb); - } - push_clip(XP, YP, WP, HP); - XP -= cx; YP -= cy; - WP = rgb->w(); HP = rgb->h(); - cache_size(rgb, WP, HP); - HDC new_gc = CreateCompatibleDC(gc_); - int save = SaveDC(new_gc); - SelectObject(new_gc, (HBITMAP)*Fl_Graphics_Driver::id(rgb)); - if ( (rgb->d() % 2) == 0 ) { - alpha_blend_(this->floor(XP), this->floor(YP), WP, HP, new_gc, 0, 0, rgb->data_w(), rgb->data_h()); - } else { - SetStretchBltMode(gc_, (Fl_Image::scaling_algorithm() == FL_RGB_SCALING_BILINEAR ? HALFTONE : BLACKONWHITE)); - StretchBlt(gc_, this->floor(XP), this->floor(YP), WP, HP, new_gc, 0, 0, rgb->data_w(), rgb->data_h(), SRCCOPY); - } - RestoreDC(new_gc, save); - DeleteDC(new_gc); - pop_clip(); -} - - -void Fl_GDI_Printer_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) { - if (Fl_Graphics_Driver::start_image(rgb, XP, YP, WP, HP, cx, cy, XP, YP, WP, HP)) { - return; - } - XFORM old_tr, tr; - GetWorldTransform(gc_, &old_tr); // storing old transform - tr.eM11 = float(rgb->w())/float(rgb->data_w()); - tr.eM22 = float(rgb->h())/float(rgb->data_h()); - tr.eM12 = tr.eM21 = 0; - tr.eDx = float(XP); - tr.eDy = float(YP); - ModifyWorldTransform(gc_, &tr, MWT_LEFTMULTIPLY); - if (*id(rgb)) { - int *pw, *ph; - cache_w_h(rgb, pw, ph); - if ( *pw != rgb->data_w() || *ph != rgb->data_h()) rgb->uncache(); - } - if (!*id(rgb)) cache(rgb); - draw_fixed(rgb, 0, 0, int(WP / tr.eM11), int(HP / tr.eM22), int(cx / tr.eM11), int(cy / tr.eM22)); - SetWorldTransform(gc_, &old_tr); -} - - -void Fl_GDI_Graphics_Driver::uncache(Fl_RGB_Image*, fl_uintptr_t &id_, fl_uintptr_t &mask_) -{ - if (id_) { - DeleteObject((HBITMAP)id_); - id_ = 0; - } - - if (mask_) { - delete_bitmask(mask_); - mask_ = 0; - } -} - -// 'fl_create_bitmap()' - Create a 1-bit bitmap for drawing... -static HBITMAP fl_create_bitmap(int w, int h, const uchar *data) { - // we need to pad the lines out to words & swap the bits - // in each byte. - int w1 = (w + 7) / 8; - int w2 = ((w + 15) / 16) * 2; - uchar* newarray = new uchar[w2*h]; - const uchar* src = data; - uchar* dest = newarray; - HBITMAP bm; - static uchar reverse[16] = /* Bit reversal lookup table */ - { 0x00, 0x88, 0x44, 0xcc, 0x22, 0xaa, 0x66, 0xee, - 0x11, 0x99, 0x55, 0xdd, 0x33, 0xbb, 0x77, 0xff }; - - for (int y = 0; y < h; y++) { - for (int n = 0; n < w1; n++, src++) - *dest++ = (uchar)((reverse[*src & 0x0f] & 0xf0) | - (reverse[(*src >> 4) & 0x0f] & 0x0f)); - dest += w2 - w1; - } - - bm = CreateBitmap(w, h, 1, 1, newarray); - - delete[] newarray; - - return bm; -} - -void Fl_GDI_Graphics_Driver::cache(Fl_Bitmap *bm) { - int *pw, *ph; - cache_w_h(bm, pw, ph); - *pw = bm->data_w(); - *ph = bm->data_h(); - *Fl_Graphics_Driver::id(bm) = (fl_uintptr_t)fl_create_bitmap(bm->data_w(), bm->data_h(), bm->array); -} - -void Fl_GDI_Graphics_Driver::draw_fixed(Fl_Pixmap *pxm, int X, int Y, int W, int H, int cx, int cy) { - X = this->floor(X); - Y = this->floor(Y); - cache_size(pxm, W, H); - cx = this->floor(cx); cy = this->floor(cy); - Fl_Region r2 = scale_clip(scale()); - if (*Fl_Graphics_Driver::mask(pxm)) { - HDC new_gc = CreateCompatibleDC(gc_); - int save = SaveDC(new_gc); - SelectObject(new_gc, (void*)*Fl_Graphics_Driver::mask(pxm)); - BitBlt(gc_, X, Y, W, H, new_gc, cx, cy, SRCAND); - SelectObject(new_gc, (void*)*Fl_Graphics_Driver::id(pxm)); - BitBlt(gc_, X, Y, W, H, new_gc, cx, cy, SRCPAINT); - RestoreDC(new_gc,save); - DeleteDC(new_gc); - } else { - float s = scale(); Fl_Graphics_Driver::scale(1); - copy_offscreen(X, Y, W, H, (Fl_Offscreen)*Fl_Graphics_Driver::id(pxm), cx, cy); - Fl_Graphics_Driver::scale(s); - } - unscale_clip(r2); -} - -/* ===== Implementation note about how Fl_Pixmap objects get printed under Windows ===== - Fl_Pixmap objects are printed with the print-specific Fl_GDI_Printer_Graphics_Driver - which uses the TransparentBlt() system function that can scale the image and treat one - of its colors as transparent. - Fl_GDI_Printer_Graphics_Driver::draw_pixmap(Fl_Pixmap *,...) sets need_pixmap_bg_color, - a static class variable, to 1 and recaches the image. This calls fl_convert_pixmap() - that checks the value of need_pixmap_bg_color. When this value is not 0, fl_convert_pixmap - runs in a way that memorizes the list of all colors in the pixmap, computes - a color absent from this list, uses it for the transparent pixels of the pixmap and puts - this color value in need_pixmap_bg_color. As a result, the transparent areas of the image - are correcty handled by the printing operation. Variable need_pixmap_bg_color is ultimately - reset to 0. - Fl_GDI_Graphics_Driver::make_unused_color_() which does the color computation mentioned - above is implemented in file src/fl_draw_pixmap.cxx - */ -void Fl_GDI_Printer_Graphics_Driver::draw_pixmap(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) { - int X, Y, W, H; - if (start_image(pxm, XP, YP, WP, HP, cx, cy, X, Y, W, H)) return; - transparent_f_type fl_TransparentBlt = TransparentBlt(); - if (fl_TransparentBlt) { - need_pixmap_bg_color = 1; - pxm->uncache(); - cache(pxm); - HDC new_gc = CreateCompatibleDC(gc_); - int save = SaveDC(new_gc); - SelectObject(new_gc, (void*)*Fl_Graphics_Driver::id(pxm)); - // print all of offscreen but its parts in background color - float scaleW = pxm->data_w()/float(pxm->w()); - float scaleH = pxm->data_h()/float(pxm->h()); - fl_TransparentBlt(gc_, X, Y, W, H, new_gc, - int(cx * scaleW), int(cy * scaleH), int(W * scaleW), int(H * scaleH), need_pixmap_bg_color ); - RestoreDC(new_gc,save); - DeleteDC(new_gc); - need_pixmap_bg_color = 0; - } - else { - copy_offscreen(X, Y, W, H, (Fl_Offscreen)*Fl_Graphics_Driver::id(pxm), cx, cy); - } -} - -// Makes an RGB triplet different from all the colors used in the pixmap -// and computes Fl_Graphics_Driver::need_pixmap_bg_color from this triplet -void Fl_GDI_Graphics_Driver::make_unused_color_(uchar &r, uchar &g, uchar &b, int color_count, void **data) { - typedef struct { uchar r; uchar g; uchar b; } UsedColor; - UsedColor *used_colors = *(UsedColor**)data; - int i; - r = 2; g = 3; b = 4; - while (1) { - for ( i=0; i= color_count) { - free((void*)used_colors); - *(UsedColor**)data = NULL; - need_pixmap_bg_color = RGB(r, g, b); - return; - } - if (r < 255) { - r++; - } else { - r = 0; - if (g < 255) { - g++; - } else { - g = 0; - b++; - } - } - } -} - -void Fl_GDI_Graphics_Driver::cache(Fl_Pixmap *img) { - Fl_Image_Surface *surf = new Fl_Image_Surface(img->data_w(), img->data_h()); - Fl_Surface_Device::push_current(surf); - uchar **pbitmap = surf->driver()->mask_bitmap(); - *pbitmap = (uchar*)1;// will instruct fl_draw_pixmap() to compute the image's mask - fl_draw_pixmap(img->data(), 0, 0, FL_BLACK); - uchar *bitmap = *pbitmap; - if (bitmap) { - *Fl_Graphics_Driver::mask(img) = - (fl_uintptr_t)create_bitmask(img->data_w(), img->data_h(), bitmap); - delete[] bitmap; - } - *pbitmap = 0; - Fl_Surface_Device::pop_current(); - Fl_Offscreen id = Fl_Graphics_Driver::get_offscreen_and_delete_image_surface(surf); - int *pw, *ph; - cache_w_h(img, pw, ph); - *pw = img->data_w(); - *ph = img->data_h(); - *Fl_Graphics_Driver::id(img) = (fl_uintptr_t)id; -} - -void Fl_GDI_Graphics_Driver::uncache_pixmap(fl_uintptr_t offscreen) { - DeleteObject((HBITMAP)offscreen); -} diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_line_style.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_line_style.cxx deleted file mode 100644 index 9d086f353..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_line_style.cxx +++ /dev/null @@ -1,111 +0,0 @@ -// -// Line style code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_GDI_Graphics_Driver_line_style.cxx - - \brief Line style drawing utility for Windows (GDI) platform. -*/ - -#include -#include -#include - -#include "Fl_GDI_Graphics_Driver.H" - - -void Fl_GDI_Graphics_Driver::line_style_unscaled(int style, int width, char* dashes) { - - // According to Bill, the "default" cap and join should be the - // "fastest" mode supported for the platform. I don't know why - // they should be different (same graphics cards, etc., right?) MRS - - static const DWORD Cap[4] = {PS_ENDCAP_FLAT, PS_ENDCAP_FLAT, PS_ENDCAP_ROUND, PS_ENDCAP_SQUARE}; - static const DWORD Join[4] = {PS_JOIN_ROUND, PS_JOIN_MITER, PS_JOIN_ROUND, PS_JOIN_BEVEL}; - - int s1 = PS_GEOMETRIC | Cap[(style>>8)&3] | Join[(style>>12)&3]; - DWORD a[16]; - int n = 0; - if (dashes && dashes[0]) { - s1 |= PS_USERSTYLE; - for (n = 0; n < 16 && *dashes; n++) a[n] = *dashes++; - } else { - s1 |= style & 0xff; // allow them to pass any low 8 bits for style - } - if ((style || n) && !width) width = int(scale()); // fix cards that do nothing for 0? - if (!width) width = 1; - if (!fl_current_xmap) color(FL_BLACK); - LOGBRUSH penbrush = {BS_SOLID,fl_RGB(),0}; // can this be fl_brush()? - HPEN newpen = ExtCreatePen(s1, width, &penbrush, n, n ? a : 0); - if (!newpen) { - Fl::error("fl_line_style(): Could not create GDI pen object."); - return; - } - HPEN oldpen = (HPEN)SelectObject(gc_, newpen); - DeleteObject(oldpen); - DeleteObject(fl_current_xmap->pen); - fl_current_xmap->pen = newpen; - style_ = style; -} - -#if USE_GDIPLUS - -void Fl_GDIplus_Graphics_Driver::line_style(int style, int width, char* dashes) { - if (!active) return Fl_Scalable_Graphics_Driver::line_style(style, width, dashes); - int gdi_width = (width ? width : 1); - pen_->SetWidth(Gdiplus::REAL(gdi_width)); - int standard_dash = style & 0x7; - if (standard_dash == FL_DASH ) - pen_->SetDashStyle(Gdiplus::DashStyleDash); - else if (standard_dash == FL_DOT ) - pen_->SetDashStyle(Gdiplus::DashStyleDot); - else if (standard_dash == FL_DASHDOT ) - pen_->SetDashStyle(Gdiplus::DashStyleDashDot); - else if (standard_dash == FL_DASHDOTDOT ) - pen_->SetDashStyle(Gdiplus::DashStyleDashDotDot); - else if(!dashes || !*dashes) - pen_->SetDashStyle(Gdiplus::DashStyleSolid); - - if (style & FL_CAP_ROUND ) { - pen_->SetStartCap(Gdiplus::LineCapRound); - pen_->SetEndCap(Gdiplus::LineCapRound); - } else if (style & FL_CAP_SQUARE ) { - pen_->SetStartCap(Gdiplus::LineCapSquare); - pen_->SetEndCap(Gdiplus::LineCapSquare); - } else { - pen_->SetStartCap(Gdiplus::LineCapFlat); - pen_->SetEndCap(Gdiplus::LineCapFlat); - } - - if (style & FL_JOIN_MITER ) { - pen_->SetLineJoin(Gdiplus::LineJoinMiter); - } else if (style & FL_JOIN_BEVEL ) { - pen_->SetLineJoin(Gdiplus::LineJoinBevel); - } else { - pen_->SetLineJoin(Gdiplus::LineJoinRound); - } - - if (dashes && *dashes) { - int n = 0; while (dashes[n]) n++; - Gdiplus::REAL *gdi_dashes = new Gdiplus::REAL[n]; - for (int i = 0; i < n; i++) gdi_dashes[i] = dashes[i]/float(gdi_width); - pen_->SetDashPattern(gdi_dashes, n); - delete[] gdi_dashes; - } - Fl_Scalable_Graphics_Driver::line_style(style, width, dashes); -} - -#endif diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_rect.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_rect.cxx deleted file mode 100644 index a86242e12..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_rect.cxx +++ /dev/null @@ -1,320 +0,0 @@ -// -// Rectangle drawing routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -/** - \file Fl_GDI_Graphics_Driver_rect.cxx - \brief Windows GDI specific line and polygon drawing with integer coordinates. - */ - -#include -#include -#include -#include -#include - -#include "Fl_GDI_Graphics_Driver.H" - - -// --- line and polygon drawing with integer coordinates - -void Fl_GDI_Graphics_Driver::point(int x, int y) { - rectf(x, y, 1, 1); -} - -void Fl_GDI_Graphics_Driver::overlay_rect(int x, int y, int w , int h) { - // make pen have a one-pixel width - line_style_unscaled( (color()==FL_WHITE?FL_SOLID:FL_DOT), 1, NULL); - int right = this->floor(x+w-1), bottom = this->floor(y+h-1); - x = this->floor(x); y = this->floor(y); - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, right, y); - LineTo(gc_, right, bottom); - LineTo(gc_, x, bottom); - LineTo(gc_, x, y); -} - -void Fl_GDI_Graphics_Driver::focus_rect(int x, int y, int w, int h) { - // Windows 95/98/ME do not implement the dotted line style, so draw - // every other pixel around the focus area... - w = floor(x+w-1) - floor(x) + 1; - h = floor(y+h-1) - floor(y) + 1; - x = floor(x); y = floor(y); - int i=1, xx, yy; - COLORREF c = fl_RGB(); - for (xx = 0; xx < w; xx++, i++) if (i & 1) SetPixel(gc_, x+xx, y, c); - for (yy = 0; yy < h; yy++, i++) if (i & 1) SetPixel(gc_, x+w, y+yy, c); - for (xx = w; xx > 0; xx--, i++) if (i & 1) SetPixel(gc_, x+xx, y+h, c); - for (yy = h; yy > 0; yy--, i++) if (i & 1) SetPixel(gc_, x, y+yy, c); -} - -void Fl_GDI_Graphics_Driver::rect_unscaled(int x, int y, int w, int h) { - if (is_solid_ && line_width_ > 1) { - line_style_unscaled(FL_CAP_SQUARE, line_width_, 0); // see issue #1052 - } - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x+w, y); - if (is_solid_ && line_width_ <= 1) LineTo(gc_, x+w, y+h+1); // see issue #1052 - LineTo(gc_, x+w, y+h); - LineTo(gc_, x, y+h); - LineTo(gc_, x, y); - if (is_solid_ && line_width_ > 1) { - line_style_unscaled(style_, line_width_, 0); - } -} - -void Fl_GDI_Graphics_Driver::rectf_unscaled(int x, int y, int w, int h) { - RECT rect; - rect.left = x; rect.top = y; - rect.right = (x + w); rect.bottom = (y + h); - FillRect(gc_, &rect, fl_brush()); -} - -void Fl_GDI_Graphics_Driver::line_unscaled(int x, int y, int x1, int y1) { - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x1, y1); - SetPixel(gc_, x1, y1, fl_RGB()); -} - -void Fl_GDI_Graphics_Driver::line_unscaled(int x, int y, int x1, int y1, int x2, int y2) { - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x1, y1); - LineTo(gc_, x2, y2); - SetPixel(gc_, x2, y2, fl_RGB()); -} - -void* Fl_GDI_Graphics_Driver::change_pen_width(int width) { // set the width of the pen, return previous pen - LOGBRUSH penbrush = {BS_SOLID, fl_RGB(), 0}; - HPEN newpen = ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_FLAT | PS_JOIN_ROUND, width, &penbrush, 0, 0); - return SelectObject(gc_, newpen); -} - -void Fl_GDI_Graphics_Driver::reset_pen_width(void *data) { - DeleteObject(SelectObject(gc_, (HPEN)data)); -} - -void Fl_GDI_Graphics_Driver::xyline_unscaled(int x, int y, int x1) { - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x1+1 , y); -} - -void Fl_GDI_Graphics_Driver::yxline_unscaled(int x, int y, int y1) { - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x, y1+1); -} - -void Fl_GDI_Graphics_Driver::loop_unscaled(int x, int y, int x1, int y1, int x2, int y2) { - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x1, y1); - LineTo(gc_, x2, y2); - LineTo(gc_, x, y); -} - -void Fl_GDI_Graphics_Driver::loop_unscaled(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { - MoveToEx(gc_, x, y, 0L); - LineTo(gc_, x1, y1); - LineTo(gc_, x2, y2); - LineTo(gc_, x3, y3); - LineTo(gc_, x, y); -} - -void Fl_GDI_Graphics_Driver::polygon_unscaled(int x, int y, int x1, int y1, int x2, int y2) { - POINT p[3]; - p[0].x = x; p[0].y = y; - p[1].x = x1; p[1].y = y1; - p[2].x = x2; p[2].y = y2; - SelectObject(gc_, fl_brush()); - Polygon(gc_, p, 3); -} - -void Fl_GDI_Graphics_Driver::polygon_unscaled(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { - POINT p[4]; - p[0].x = x; p[0].y = y; - p[1].x = x1; p[1].y = y1; - p[2].x = x2; p[2].y = y2; - p[3].x = x3; p[3].y = y3; - SelectObject(gc_, fl_brush()); - Polygon(gc_, p, 4); -} - -// --- clipping - -void Fl_GDI_Graphics_Driver::push_clip(int x, int y, int w, int h) { - HRGN r; - if (w > 0 && h > 0) { - r = (HRGN)XRectangleRegion(x,y,w,h); - HRGN current = (HRGN)rstack[rstackptr]; - if (current) { - CombineRgn(r,r,current,RGN_AND); - } - } else { // make empty clip region: - r = CreateRectRgn(0,0,0,0); - } - if (rstackptr < region_stack_max) rstack[++rstackptr] = r; - else Fl::warning("Fl_GDI_Graphics_Driver::push_clip: clip stack overflow!\n"); - fl_restore_clip(); -} - -int Fl_GDI_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){ - X = x; Y = y; W = w; H = h; - HRGN r = (HRGN)rstack[rstackptr]; - if (!r) return 0; - // The win32 API makes no distinction between partial and complete - // intersection, so we have to check for partial intersection ourselves. - // However, given that the regions may be composite, we have to do - // some voodoo stuff... - HRGN rr = (HRGN)XRectangleRegion(x,y,w,h); - HRGN temp = CreateRectRgn(0,0,0,0); - int ret; - if (CombineRgn(temp, rr, r, RGN_AND) == NULLREGION) { // disjoint - W = H = 0; - ret = 2; - } else if (EqualRgn(temp, rr)) { // complete - ret = 0; - } else { // partial intersection - RECT rect; - GetRgnBox(temp, &rect); - if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) { // if print context, convert coords from device to logical - POINT pt[2] = { {rect.left, rect.top}, {rect.right, rect.bottom} }; - DPtoLP(gc_, pt, 2); - X = pt[0].x; Y = pt[0].y; W = pt[1].x - X; H = pt[1].y - Y; - } - else { - X = rect.left; Y = rect.top; W = rect.right - X; H = rect.bottom - Y; - } - ret = 1; - } - DeleteObject(temp); - DeleteObject(rr); - return ret; -} - -int Fl_GDI_Graphics_Driver::not_clipped(int x, int y, int w, int h) { - if (x+w <= 0 || y+h <= 0) return 0; - HRGN r = (HRGN)rstack[rstackptr]; - if (!r) return 1; - RECT rect; - if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) { // in case of print context, convert coords from logical to device - POINT pt[2] = { {x, y}, {x + w, y + h} }; - LPtoDP(gc_, pt, 2); - rect.left = pt[0].x; rect.top = pt[0].y; rect.right = pt[1].x; rect.bottom = pt[1].y; - } else { - rect.left = x; rect.top = y; rect.right = x+w; rect.bottom = y+h; - } - return RectInRegion(r,&rect); -} - -void Fl_GDI_Graphics_Driver::restore_clip() { - fl_clip_state_number++; - if (gc_) { - HRGN r = NULL; - if (rstack[rstackptr]) r = (HRGN)scale_clip(scale()); - SelectClipRgn(gc_, (HRGN)rstack[rstackptr]); // if region is NULL, clip is automatically cleared - if (r) unscale_clip(r); - } -} - -#if USE_GDIPLUS - -void Fl_GDIplus_Graphics_Driver::line(int x, int y, int x1, int y1) { - if (!active) return Fl_Scalable_Graphics_Driver::line(x, y, x1, y1); - bool AA = !(x == x1 || y == y1); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - pen_->SetColor(gdiplus_color_); - if (AA) graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.DrawLine(pen_, x, y, x1, y1); -} - -void Fl_GDIplus_Graphics_Driver::line(int x, int y, int x1, int y1, int x2, int y2) { - if (!active) return Fl_Scalable_Graphics_Driver::line(x, y, x1, y1, x2, y2); - line(x, y, x1, y1); - line(x1, y1, x2, y2); -} - -void Fl_GDIplus_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) { - if (!active) return Fl_Scalable_Graphics_Driver::loop(x0, y0, x1, y1, x2, y2); - Gdiplus::GraphicsPath path; - Gdiplus::Point gdi2_p[3] = {Gdiplus::Point(x0, y0), Gdiplus::Point(x1, y1), Gdiplus::Point(x2, y2)}; - path.AddLines(gdi2_p, 3); - path.CloseFigure(); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - pen_->SetColor(gdiplus_color_); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.DrawPath(pen_, &path); -} - -#define fl_min(a,b) (a < b ? a : b) -#define fl_max(a,b) (a > b ? a : b) -void Fl_GDIplus_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { - if ( (x0 == x3 && x1 == x2 && y0 == y1 && y3 == y2) || - (x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) ) { // rectangular loop - int left = fl_min(x0, fl_min(x1, fl_min(x2, x3))); - int right = fl_max(x0, fl_max(x1, fl_max(x2, x3))); - int top = fl_min(y0, fl_min(y1, fl_min(y2, y3))); - int bottom = fl_max(y0, fl_max(y1, fl_max(y2, y3))); - rect(left, top, right-left+1, bottom-top+1); - } else { - if (!active) return Fl_Scalable_Graphics_Driver::loop(x0, y0, x1, y1, x2, y2, x3, y3); - Gdiplus::GraphicsPath path; - Gdiplus::PointF gdi2_p[4] = {Gdiplus::PointF(x0+1-line_width_/2.f, y0+1-line_width_/2.f), Gdiplus::PointF(x1+1-line_width_/2.f, y1+1-line_width_/2.f), Gdiplus::PointF(x2+1-line_width_/2.f, y2+1-line_width_/2.f), Gdiplus::PointF(x3+1-line_width_/2.f, y3+1-line_width_/2.f)}; - path.AddLines(gdi2_p, 4); - path.CloseFigure(); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - pen_->SetColor(gdiplus_color_); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.DrawPath(pen_, &path); - } -} - -void Fl_GDIplus_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) { - if (!active) return Fl_Scalable_Graphics_Driver::polygon(x0, y0, x1, y1, x2, y2); - Gdiplus::GraphicsPath path; - path.AddLine(x0, y0, x1, y1); - path.AddLine(x1, y1, x2, y2); - path.CloseFigure(); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - brush_->SetColor(gdiplus_color_); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.FillPath(brush_, &path); -} - -void Fl_GDIplus_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { - if ( (x0 == x3 && x1 == x2 && y0 == y1 && y3 == y2) || - (x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) ) { - int left = fl_min(x0, fl_min(x1, fl_min(x2, x3))); - int right = fl_max(x0, fl_max(x1, fl_max(x2, x3))); - int top = fl_min(y0, fl_min(y1, fl_min(y2, y3))); - int bottom = fl_max(y0, fl_max(y1, fl_max(y2, y3))); - rectf(left, top, right-left, bottom-top); - } else { - if (!active) return Fl_Scalable_Graphics_Driver::polygon(x0, y0, x1, y1, x2, y2, x3, y3); - Gdiplus::GraphicsPath path; - path.AddLine(x0, y0, x1, y1); - path.AddLine(x1, y1, x2, y2); - path.AddLine(x2, y2, x3, y3); - path.CloseFigure(); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - brush_->SetColor(gdiplus_color_); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - graphics_.FillPath(brush_, &path); - } -} -#endif diff --git a/src/drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx b/src/drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx deleted file mode 100644 index e469bb11b..000000000 --- a/src/drivers/GDI/Fl_GDI_Graphics_Driver_vertex.cxx +++ /dev/null @@ -1,231 +0,0 @@ -// -// Portable drawing routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_GDI_Graphics_Driver_vertex.cxx - - \brief Portable drawing code for drawing arbitrary shapes with - simple 2D transformations, implemented for Windows GDI. -*/ - -#include "Fl_GDI_Graphics_Driver.H" - -#include -#include -#include - - -void Fl_GDI_Graphics_Driver::end_points() { - for (int i=0; i1) Polyline(gc_, long_point, n); -} - -void Fl_GDI_Graphics_Driver::end_loop() { - fixloop(); - if (n>2) transformed_vertex0(float(long_point[0].x), float(long_point[0].y)); - end_line(); -} - -void Fl_GDI_Graphics_Driver::end_polygon() { - fixloop(); - if (n < 3) { - end_line(); - return; - } - if (n>2) { - SelectObject(gc_, fl_brush()); - Polygon(gc_, long_point, n); - } -} - -void Fl_GDI_Graphics_Driver::begin_complex_polygon() { - Fl_Graphics_Driver::begin_complex_polygon(); - numcount = 0; -} - -void Fl_GDI_Graphics_Driver::gap() { - while (n>gap_+2 && long_point[n-1].x == long_point[gap_].x && long_point[n-1].y == long_point[gap_].y) n--; - if (n > gap_+2) { - transformed_vertex0(float(long_point[gap_].x), float(long_point[gap_].y)); - counts[numcount++] = n-gap_; - gap_ = n; - } else { - n = gap_; - } -} - -void Fl_GDI_Graphics_Driver::end_complex_polygon() { - gap(); - if (n < 3) { - end_line(); - return; - } - if (n>2) { - SelectObject(gc_, fl_brush()); - PolyPolygon(gc_, long_point, counts, numcount); - } -} - -void Fl_GDI_Graphics_Driver::ellipse_unscaled(double xt, double yt, double rx, double ry) { - int llx = (int)rint(xt-rx); - int w = (int)rint(xt+rx)-llx; - int lly = (int)rint(yt-ry); - int h = (int)rint(yt+ry)-lly; - - if (what==POLYGON) { - SelectObject(gc_, fl_brush()); - Pie(gc_, llx, lly, llx+w, lly+h, 0,0, 0,0); - } else - Arc(gc_, llx, lly, llx+w, lly+h, 0,0, 0,0); -} - -#if USE_GDIPLUS - -void Fl_GDIplus_Graphics_Driver::transformed_vertex(double xf, double yf) { - if (!active) return Fl_Scalable_Graphics_Driver::transformed_vertex(xf, yf); - transformed_vertex0(float(xf) , float(yf) ); -} - -void Fl_GDIplus_Graphics_Driver::vertex(double x,double y) { - if (!active) return Fl_Scalable_Graphics_Driver::vertex(x, y); - transformed_vertex0(float(x*m.a + y*m.c + m.x) , float(x*m.b + y*m.d + m.y) ); -} - -void Fl_GDIplus_Graphics_Driver::end_points() { - if (!active) return Fl_GDI_Graphics_Driver::end_points(); - for (int i = 0; i < n; i++) point(long_point[i].x, long_point[i].y); -} - -void Fl_GDIplus_Graphics_Driver::end_line() { - if (!active) return Fl_GDI_Graphics_Driver::end_line(); - if (n < 2) { - end_points(); - return; - } - if (n>1) { - Gdiplus::GraphicsPath path; - Gdiplus::Point *gdi2_p = new Gdiplus::Point[n]; - for (int i = 0; i < n; i++) { - gdi2_p[i] = Gdiplus::Point(long_point[i].x, long_point[i].y); - } - path.AddLines(gdi2_p, n); - delete[] gdi2_p; - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - pen_->SetColor(gdiplus_color_); - graphics_.DrawPath(pen_, &path); - } -} - -void Fl_GDIplus_Graphics_Driver::end_loop() { - if (!active) return Fl_GDI_Graphics_Driver::end_loop(); - fixloop(); - if (n >= 2) { - Gdiplus::GraphicsPath path; - Gdiplus::Point *gdi2_p = new Gdiplus::Point[n]; - for (int i = 0; i < n; i++) { - gdi2_p[i] = Gdiplus::Point(long_point[i].x, long_point[i].y); - } - path.AddLines(gdi2_p, n); - path.CloseFigure(); - delete[] gdi2_p; - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - pen_->SetColor(gdiplus_color_); - graphics_.DrawPath(pen_, &path); - } -} - -void Fl_GDIplus_Graphics_Driver::end_polygon() { - if (!active) return Fl_GDI_Graphics_Driver::end_polygon(); - fixloop(); - if (n < 3) { - end_line(); - return; - } - if (n>2) { - Gdiplus::GraphicsPath path; - Gdiplus::Point *gdi2_p = new Gdiplus::Point[n]; - for (int i = 0; i < n; i++) { - gdi2_p[i] = Gdiplus::Point(long_point[i].x, long_point[i].y); - } - path.AddPolygon(gdi2_p, n); - delete[] gdi2_p; - path.CloseFigure(); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - brush_->SetColor(gdiplus_color_); - graphics_.FillPath(brush_, &path); - } -} - -void Fl_GDIplus_Graphics_Driver::end_complex_polygon() { - if (!active) return Fl_GDI_Graphics_Driver::end_complex_polygon(); - gap(); - if (n < 3) { - end_line(); - return; - } - if (n>2) { - Gdiplus::GraphicsPath path; - Gdiplus::Point *gdi2_p = new Gdiplus::Point[n]; - for (int i = 0; i < n; i++) { - gdi2_p[i] = Gdiplus::Point(long_point[i].x, long_point[i].y); - } - path.AddPolygon(gdi2_p, n); - delete[] gdi2_p; - path.CloseFigure(); - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - brush_->SetColor(gdiplus_color_); - graphics_.FillPath(brush_, &path); - } -} - -void Fl_GDIplus_Graphics_Driver::circle(double x, double y, double r) { - if (!active) return Fl_Scalable_Graphics_Driver::circle(x, y, r); - double xt = transform_x(x,y); - double yt = transform_y(x,y); - double rx = r * (m.c ? sqrt(m.a*m.a+m.c*m.c) : fabs(m.a)); - double ry = r * (m.b ? sqrt(m.b*m.b+m.d*m.d) : fabs(m.d)); - int llx = (int)rint(xt-rx); - int w = (int)rint(xt+rx)-llx; - int lly = (int)rint(yt-ry); - int h = (int)rint(yt+ry)-lly; - Gdiplus::Graphics graphics_(gc_); - graphics_.ScaleTransform(scale(), scale()); - graphics_.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); - if (what==POLYGON) { - brush_->SetColor(gdiplus_color_); - graphics_.FillPie(brush_, llx, lly, w, h, 0, 360); - } else { - pen_->SetColor(gdiplus_color_); - graphics_.DrawArc(pen_, llx, lly, w, h, 0, 360); - } -} -#endif - diff --git a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H b/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H deleted file mode 100644 index 129a4ecbc..000000000 --- a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.H +++ /dev/null @@ -1,44 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// Copyright 2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef FL_GDI_IMAGE_SURFACE_DRIVER_H -#define FL_GDI_IMAGE_SURFACE_DRIVER_H - -#include -#include -#include - -class Fl_GDI_Image_Surface_Driver : public Fl_Image_Surface_Driver { - void end_current() FL_OVERRIDE; -public: - HWND pre_window; - int _savedc; - void mask(const Fl_RGB_Image *) FL_OVERRIDE; - struct shape_data_type { - HBITMAP background; - uchar *vBits; - Fl_RGB_Image* mask; - } *shape_data_; - Fl_GDI_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off); - ~Fl_GDI_Image_Surface_Driver(); - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; - Fl_RGB_Image *image() FL_OVERRIDE; - POINT origin; -}; - -#endif // FL_GDI_IMAGE_SURFACE_DRIVER_H diff --git a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx b/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx deleted file mode 100644 index a14524ee2..000000000 --- a/src/drivers/GDI/Fl_GDI_Image_Surface_Driver.cxx +++ /dev/null @@ -1,169 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include "Fl_GDI_Graphics_Driver.H" -#include "../WinAPI/Fl_WinAPI_Screen_Driver.H" -#include "Fl_GDI_Image_Surface_Driver.H" -#include -#include -#include - - -Fl_GDI_Image_Surface_Driver::Fl_GDI_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) { - Fl_Display_Device::display_device(); // make sure fl_graphics_driver was initialized - float d = fl_graphics_driver->scale(); - if (!off && d != 1 && high_res) { - w = int(w*d); - h = int(h*d); - } - HDC gc = (HDC)Fl_Graphics_Driver::default_driver().gc(); - offscreen = off ? off : (Fl_Offscreen)CreateCompatibleBitmap( (gc ? gc : fl_GetDC(0) ) , w, h); - if (!offscreen) offscreen = (Fl_Offscreen)CreateCompatibleBitmap(fl_GetDC(0), w, h); - driver(Fl_Graphics_Driver::newMainGraphicsDriver()); - if (d != 1 && high_res) ((Fl_GDI_Graphics_Driver*)driver())->scale(d); - origin.x = origin.y = 0; - shape_data_ = NULL; -} - - -Fl_GDI_Image_Surface_Driver::~Fl_GDI_Image_Surface_Driver() { - if (shape_data_ && shape_data_->background) { - DeleteObject(shape_data_->background); - delete shape_data_->mask; - free(shape_data_); - } - if (offscreen && !external_offscreen) DeleteObject((HBITMAP)offscreen); - delete driver(); -} - - -void Fl_GDI_Image_Surface_Driver::set_current() { - HDC gc = fl_makeDC((HBITMAP)offscreen); - driver()->gc(gc); - SetWindowOrgEx(gc, origin.x, origin.y, NULL); - Fl_Surface_Device::set_current(); - pre_window = fl_window; - _savedc = SaveDC(gc); - fl_window=(HWND)offscreen; -} - - -void Fl_GDI_Image_Surface_Driver::translate(int x, int y) { - ((Fl_GDI_Graphics_Driver*)driver())->translate_all(x, y); -} - - -void Fl_GDI_Image_Surface_Driver::untranslate() { - ((Fl_GDI_Graphics_Driver*)driver())->untranslate_all(); -} - - -Fl_RGB_Image* Fl_GDI_Image_Surface_Driver::image() -{ - if (shape_data_ && shape_data_->background) { - // get the offscreen size in pixels - HDC gc = fl_makeDC((HBITMAP)offscreen); - BITMAPINFO bmi; - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 0; - bmi.bmiHeader.biSizeImage = 0; - GetDIBits(gc, (HBITMAP)offscreen, 0, 0, NULL, &bmi, DIB_RGB_COLORS); - int W = bmi.bmiHeader.biWidth; - int H = bmi.bmiHeader.biHeight; - int line_size = ((3*W+3)/4) * 4; - - // read bits of main offscreen - uchar *dib_src = new uchar[line_size * H]; - bmi.bmiHeader.biWidth = W; - bmi.bmiHeader.biHeight = H; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biBitCount = 24; - GetDIBits(gc, (HBITMAP)offscreen, 0, H, - dib_src, &bmi, DIB_RGB_COLORS); - - // draw above the secondary offscreen the main offscreen masked by shape_data_->mask - GdiFlush(); - Fl_Image_Surface_Driver::copy_with_mask(shape_data_->mask, shape_data_->vBits, dib_src, ((3*W+3)/4) * 4, true); - delete shape_data_->mask; - delete[] dib_src; - - // write bits of main offscreen - SetDIBits(gc, (HBITMAP)offscreen, 0, H, shape_data_->vBits, &bmi, DIB_RGB_COLORS); - DeleteDC(gc); - DeleteObject(shape_data_->background); - shape_data_->background = NULL; - free(shape_data_); - shape_data_ = NULL; - } - Fl_RGB_Image *image = Fl::screen_driver()->read_win_rectangle( 0, 0, width, height, 0); - return image; -} - - -void Fl_GDI_Image_Surface_Driver::end_current() -{ - HDC gc = (HDC)driver()->gc(); - GetWindowOrgEx(gc, &origin); - RestoreDC(gc, _savedc); - DeleteDC(gc); - fl_window = pre_window; - Fl_Surface_Device::end_current(); -} - - -void Fl_GDI_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) { - shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type)); - // get the offscreen size in pixels - HDC gc = fl_makeDC((HBITMAP)offscreen); - BITMAPINFO bmi; - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 0; - bmi.bmiHeader.biSizeImage = 0; - - GetDIBits(gc, (HBITMAP)offscreen, 0, 0, NULL, &bmi, DIB_RGB_COLORS); - int W = bmi.bmiHeader.biWidth; - int H = bmi.bmiHeader.biHeight; - - shape_data_->mask = Fl_Image_Surface_Driver::RGB3_to_RGB1(mask, W, H); - - // duplicate current offscreen content to new offscreen - int line_size = ((3*W+3)/4) * 4; - uchar *dib = new uchar[line_size * H]; // create temporary buffer to read DIB - bmi.bmiHeader.biWidth = W; - bmi.bmiHeader.biHeight = H; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biBitCount = 24; - - GetDIBits(gc, (HBITMAP)offscreen, 0, H, dib, &bmi, DIB_RGB_COLORS); - - HDC background_gc = CreateCompatibleDC(gc); - shape_data_->background = - CreateDIBSection(background_gc, &bmi, DIB_RGB_COLORS, - (void**)&shape_data_->vBits, NULL, 0); - if (!shape_data_->background) { - Fl::error("CreateDIBSection error=%lu", GetLastError()); - } - memcpy(shape_data_->vBits, dib, H * line_size); - delete[] dib; - DeleteDC(background_gc); - DeleteDC(gc); -} - diff --git a/src/drivers/PostScript/Fl_PostScript.cxx b/src/drivers/PostScript/Fl_PostScript.cxx index 324d1c6f9..9cece371d 100644 --- a/src/drivers/PostScript/Fl_PostScript.cxx +++ b/src/drivers/PostScript/Fl_PostScript.cxx @@ -38,8 +38,8 @@ #if USE_PANGO #include // for M_PI #include -#include -#include +#include +#include #include # if ! PANGO_VERSION_CHECK(1,10,0) # error "Requires Pango 1.10 or higher" @@ -1470,7 +1470,7 @@ void Fl_PostScript_Graphics_Driver::ps_untranslate(void) fprintf(output, "GR GR\n"); } -#if defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND) +#if defined(FLTK_USE_X11) Fl_Paged_Device *Fl_PDF_File_Surface::new_platform_pdf_surface_(const char ***pfname) { *pfname = NULL; @@ -1492,7 +1492,7 @@ int Fl_PDF_File_Surface::begin_document(const char* defaultfilename, return begin_job(NULL, perr_message); } -#endif // defined(FLTK_USE_X11) || defined(FLTK_USE_WAYLAND) +#endif // defined(FLTK_USE_X11) # else // USE_PANGO diff --git a/src/drivers/PostScript/Fl_PostScript_image.cxx b/src/drivers/PostScript/Fl_PostScript_image.cxx index 9b5fe1668..ebb91cf03 100644 --- a/src/drivers/PostScript/Fl_PostScript_image.cxx +++ b/src/drivers/PostScript/Fl_PostScript_image.cxx @@ -26,7 +26,7 @@ #include // memcpy() #if USE_PANGO -# include +# include #else # include // fprintf() #endif diff --git a/src/drivers/Quartz/Fl_Font.H b/src/drivers/Quartz/Fl_Font.H deleted file mode 100644 index f1a710eea..000000000 --- a/src/drivers/Quartz/Fl_Font.H +++ /dev/null @@ -1,42 +0,0 @@ -// -// Font definitions for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Two internal fltk data structures: -// -// Fl_Fontdesc: an entry into the fl_font() table. There is one of these -// for each fltk font number. -// -#ifndef FL_FONT_ -#define FL_FONT_ - -#include -#include "Fl_Quartz_Graphics_Driver.H" -#include "../../Fl_Scalable_Graphics_Driver.H" // Fl_Font_Descriptor -#include - -class Fl_Quartz_Font_Descriptor : public Fl_Font_Descriptor { -public: - Fl_Quartz_Font_Descriptor(const char* fontname, Fl_Fontsize size); - virtual FL_EXPORT ~Fl_Quartz_Font_Descriptor(); - CTFontRef fontref; - // the unicode span is divided in 512 blocks of 128 characters - float *width[512]; // array of arrays of character widths - short q_width; -}; - -extern FL_EXPORT Fl_Fontdesc *fl_fonts; // the table - -#endif diff --git a/src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H b/src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H deleted file mode 100644 index 8ef586624..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H +++ /dev/null @@ -1,39 +0,0 @@ -// -// Definition of Apple Quartz graphics driver -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2016 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#ifndef Fl_Quartz_Copy_Surface_Driver_H -#define Fl_Quartz_Copy_Surface_Driver_H - -#include -#include - -class Fl_Quartz_Copy_Surface_Driver : public Fl_Copy_Surface_Driver { - friend class Fl_Copy_Surface_Driver; -protected: - CFMutableDataRef pdfdata; - CGContextRef gc; - static size_t MyPutBytes(void* info, const void* buffer, size_t count); - Fl_Quartz_Copy_Surface_Driver(int w, int h); - // implemented in Fl_cocoa.mm because uses Objective-c - ~Fl_Quartz_Copy_Surface_Driver(); - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; -}; - -#endif /* Fl_Quartz_Copy_Surface_Driver_H */ diff --git a/src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.cxx b/src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.cxx deleted file mode 100644 index 77ecdaa0a..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.cxx +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2019 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include -#include "Fl_Quartz_Graphics_Driver.H" -#include "Fl_Quartz_Copy_Surface_Driver.H" -#include "../Cocoa/Fl_Cocoa_Window_Driver.H" - - -Fl_Quartz_Copy_Surface_Driver::Fl_Quartz_Copy_Surface_Driver(int w, int h) : Fl_Copy_Surface_Driver(w, h) { - driver(new Fl_Quartz_Printer_Graphics_Driver); - pdfdata = CFDataCreateMutable(NULL, 0); - CGDataConsumerRef myconsumer; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 - if (&CGDataConsumerCreateWithCFData != NULL) { - myconsumer = CGDataConsumerCreateWithCFData(pdfdata); // 10.4 - } - else -#endif - { - static CGDataConsumerCallbacks callbacks = { Fl_Quartz_Copy_Surface_Driver::MyPutBytes, NULL }; - myconsumer = CGDataConsumerCreate((void*) pdfdata, &callbacks); - } - float d = fl_graphics_driver->scale(); - CGRect bounds = CGRectMake(0, 0, w * d, h * d); - gc = CGPDFContextCreate(myconsumer, &bounds, NULL); - CGDataConsumerRelease(myconsumer); - if (gc) { - CGContextBeginPage(gc, &bounds); - CGContextScaleCTM(gc, d, -d); - CGContextTranslateCTM(gc, 0.5, -h + 0.5); - CGContextSaveGState(gc); - } -} - -void Fl_Quartz_Copy_Surface_Driver::set_current() { - driver()->gc(gc); - fl_window = (FLWindow*)1; - Fl_Surface_Device::set_current(); -} - -size_t Fl_Quartz_Copy_Surface_Driver::MyPutBytes(void* info, const void* buffer, size_t count) -{ - CFDataAppendBytes ((CFMutableDataRef) info, (const UInt8 *)buffer, count); - return count; -} - -void Fl_Quartz_Copy_Surface_Driver::translate(int x, int y) { - CGContextRestoreGState(gc); - CGContextSaveGState(gc); - CGContextTranslateCTM(gc, x, y); - CGContextSaveGState(gc); -} - -void Fl_Quartz_Copy_Surface_Driver::untranslate() { - CGContextRestoreGState(gc); -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver.H b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver.H deleted file mode 100644 index 0afc7369e..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver.H +++ /dev/null @@ -1,149 +0,0 @@ -// -// Definition of Apple Quartz graphics driver -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Quartz_Graphics_Driver.H - \brief Definition of Apple Quartz graphics driver. - */ - -#ifndef FL_QUARTZ_GRAPHICS_DRIVER_H -#define FL_QUARTZ_GRAPHICS_DRIVER_H - -#include -#include -#include - -struct Fl_Fontdesc; -class Fl_Quartz_Font_Descriptor; - -/** - \brief The Mac OS X-specific graphics class. - - This class is implemented only on the Mac OS X platform. - */ -class Fl_Quartz_Graphics_Driver : public Fl_Graphics_Driver { - friend class Fl_Cocoa_Printer_Driver; - friend class Fl_Quartz_Font_Descriptor; -protected: - CGContextRef gc_; - bool high_resolution_; - float quartz_line_width_; - CGLineCap quartz_line_cap_; - CGLineJoin quartz_line_join_; - CGFloat *quartz_line_pattern; - int quartz_line_pattern_size; - void cache_size(Fl_Image* img, int &width, int &height) FL_OVERRIDE; -public: - Fl_Quartz_Graphics_Driver(); - int has_feature(driver_feature mask) FL_OVERRIDE { return mask & NATIVE; } - void gc(void *ctxt) FL_OVERRIDE { gc_ = (CGContextRef)ctxt; global_gc(); } - void *gc() FL_OVERRIDE {return gc_;} - char can_do_alpha_blending() FL_OVERRIDE; - - // --- bitmap stuff - static CGImageRef create_bitmask(int w, int h, const uchar *array); // NOT virtual - void delete_bitmask(fl_uintptr_t bm) FL_OVERRIDE; - void draw_pixmap(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_bitmap(Fl_Bitmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_rgb(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; - void draw_image(const uchar* buf, int X,int Y,int W,int H, int D=3, int L=0) FL_OVERRIDE; - void draw_image(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=3) FL_OVERRIDE; - void draw_image_mono(const uchar* buf, int X,int Y,int W,int H, int D=1, int L=0) FL_OVERRIDE; - void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=1) FL_OVERRIDE; - void cache(Fl_Pixmap *img) FL_OVERRIDE; - void cache(Fl_Bitmap *img) FL_OVERRIDE; - void cache(Fl_RGB_Image *img) FL_OVERRIDE; - void uncache(Fl_RGB_Image *img, fl_uintptr_t &id_, fl_uintptr_t &mask_) FL_OVERRIDE; - void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) FL_OVERRIDE; - void draw_CGImage(CGImageRef cgimg, int x, int y, int w, int h, int srcx, int srcy, int sw, int sh); - static CGRect fl_cgrectmake_cocoa(int x, int y, int w, int h); - void add_rectangle_to_region(Fl_Region r, int x, int y, int w, int h) FL_OVERRIDE; - Fl_Region XRectangleRegion(int x, int y, int w, int h) FL_OVERRIDE; - void XDestroyRegion(Fl_Region r) FL_OVERRIDE; - void high_resolution(bool b) { high_resolution_ = b; } -protected: - void point(int x, int y) FL_OVERRIDE; - void rect(int x, int y, int w, int h) FL_OVERRIDE; - void focus_rect(int x, int y, int w, int h) FL_OVERRIDE; - void rectf(int x, int y, int w, int h) FL_OVERRIDE; - void line(int x, int y, int x1, int y1) FL_OVERRIDE; - void line(int x, int y, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void xyline(int x, int y, int x1) FL_OVERRIDE; - void xyline(int x, int y, int x1, int y2) FL_OVERRIDE; - void xyline(int x, int y, int x1, int y2, int x3) FL_OVERRIDE; - void yxline(int x, int y, int y1) FL_OVERRIDE; - void yxline(int x, int y, int y1, int x2) FL_OVERRIDE; - void yxline(int x, int y, int y1, int x2, int y3) FL_OVERRIDE; - void loop(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; - void polygon(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; - void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; - // --- clipping - void push_clip(int x, int y, int w, int h) FL_OVERRIDE; - int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) FL_OVERRIDE; - int not_clipped(int x, int y, int w, int h) FL_OVERRIDE; - void restore_clip() FL_OVERRIDE; - void end_points() FL_OVERRIDE; - void end_line() FL_OVERRIDE; - void end_polygon() FL_OVERRIDE; - void end_complex_polygon() FL_OVERRIDE; - void circle(double x, double y, double r) FL_OVERRIDE; - void arc(int x, int y, int w, int h, double a1, double a2) FL_OVERRIDE; - void pie(int x, int y, int w, int h, double a1, double a2) FL_OVERRIDE; - void line_style(int style, int width=0, char* dashes=0) FL_OVERRIDE; - void color(Fl_Color c) FL_OVERRIDE; - Fl_Color color() FL_OVERRIDE { return color_; } - void color(uchar r, uchar g, uchar b) FL_OVERRIDE; - void draw(const char *str, int n, int x, int y) FL_OVERRIDE; - void draw(const char *str, int n, float x, float y) FL_OVERRIDE; - void draw(int angle, const char *str, int n, int x, int y) FL_OVERRIDE; - double width(const UniChar* txt, int n); - void rtl_draw(const char *str, int n, int x, int y) FL_OVERRIDE; - void font(Fl_Font face, Fl_Fontsize fsize) FL_OVERRIDE; - double width(const char *str, int n) FL_OVERRIDE; - double width(unsigned int c) FL_OVERRIDE; - int height() FL_OVERRIDE; - int descent() FL_OVERRIDE; - virtual bool high_resolution() { return high_resolution_; } - void global_gc() FL_OVERRIDE; - void quartz_restore_line_style(); - inline Fl_Quartz_Font_Descriptor *valid_font_descriptor(); - const char* get_font_name(Fl_Font fnum, int* ap) FL_OVERRIDE; - int get_font_sizes(Fl_Font fnum, int*& sizep) FL_OVERRIDE; - const char *font_name(int num) FL_OVERRIDE; - void font_name(int num, const char *name) FL_OVERRIDE; - Fl_Fontdesc* calc_fl_fonts(void) FL_OVERRIDE; - - void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h) FL_OVERRIDE; - Fl_Font set_fonts(const char* xstarname) FL_OVERRIDE; - void set_fontname_in_fontdesc(Fl_Fontdesc *f); - void uncache_pixmap(fl_uintptr_t p) FL_OVERRIDE; - - void descriptor_init(const char* name, Fl_Fontsize Size, Fl_Quartz_Font_Descriptor *d); - void overlay_rect(int x, int y, int w , int h) FL_OVERRIDE; - float override_scale() FL_OVERRIDE; - void restore_scale(float) FL_OVERRIDE; - void antialias(int state) FL_OVERRIDE; - int antialias() FL_OVERRIDE; -}; - -class Fl_Quartz_Printer_Graphics_Driver : public Fl_Quartz_Graphics_Driver { -public: - int has_feature(driver_feature mask) FL_OVERRIDE { return mask & (NATIVE | PRINTER); } -}; - -#endif // FL_QUARTZ_GRAPHICS_DRIVER_H diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver.cxx deleted file mode 100644 index 21c5eda4c..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver.cxx +++ /dev/null @@ -1,133 +0,0 @@ -// -// Rectangle drawing routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include "Fl_Quartz_Graphics_Driver.H" -#include "../Darwin/Fl_Darwin_System_Driver.H" -#include "../Cocoa/Fl_Cocoa_Screen_Driver.H" -#include -#include -#include - - -void Fl_Quartz_Graphics_Driver::antialias(int state) { -} - -int Fl_Quartz_Graphics_Driver::antialias() { - return 1; -} - -Fl_Quartz_Graphics_Driver::Fl_Quartz_Graphics_Driver() : Fl_Graphics_Driver(), gc_(NULL) { - quartz_line_width_ = 1.f; - quartz_line_cap_ = kCGLineCapButt; - quartz_line_join_ = kCGLineJoinMiter; - quartz_line_pattern = 0; - quartz_line_pattern_size = 0; - high_resolution_ = false; -} - -char Fl_Quartz_Graphics_Driver::can_do_alpha_blending() { - return 1; -} - -static void bmProviderRelease (void *src, const void *data, size_t size) { - CFIndex count = CFGetRetainCount(src); - CFRelease(src); - if(count == 1) free((void*)data); -} - -/* Reference to the current CGContext - For back-compatibility only. The preferred procedure to get this reference is - Fl_Surface_Device::surface()->driver()->gc(). - */ -CGContextRef fl_gc = 0; - -void Fl_Quartz_Graphics_Driver::global_gc() -{ - fl_gc = (CGContextRef)gc(); -} - - -CGContextRef fl_mac_gc() { return fl_gc; } - - -void Fl_Quartz_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen osrc, int srcx, int srcy) { - // draw portion srcx,srcy,w,h of osrc to position x,y (top-left) of the graphics driver's surface - CGContextRef src = (CGContextRef)osrc; - int sw = (int)CGBitmapContextGetWidth(src); - int sh = (int)CGBitmapContextGetHeight(src); - CGImageRef img; - img = CGBitmapContextCreateImage(src); // requires 10.4 - CGAffineTransform at = CGContextGetCTM(src); - float s = at.a; - draw_CGImage(img, x, y, w, h, srcx, srcy, sw/s, sh/s); - CGImageRelease(img); -} - -// so a CGRect matches exactly what is denoted x,y,w,h for clipping purposes -CGRect Fl_Quartz_Graphics_Driver::fl_cgrectmake_cocoa(int x, int y, int w, int h) { - return CGRectMake(x - 0.5, y - 0.5, w, h); -} - -void Fl_Quartz_Graphics_Driver::add_rectangle_to_region(Fl_Region r_, int X, int Y, int W, int H) { - struct flCocoaRegion *r = (struct flCocoaRegion*)r_; - CGRect arg = Fl_Quartz_Graphics_Driver::fl_cgrectmake_cocoa(X, Y, W, H); - int j; // don't add a rectangle totally inside the Fl_Region - for(j = 0; j < r->count; j++) { - if(CGRectContainsRect(r->rects[j], arg)) break; - } - if( j >= r->count) { - r->rects = (CGRect*)realloc(r->rects, (++(r->count)) * sizeof(CGRect)); - r->rects[r->count - 1] = arg; - } -} - -Fl_Region Fl_Quartz_Graphics_Driver::XRectangleRegion(int x, int y, int w, int h) { - struct flCocoaRegion* R = (struct flCocoaRegion*)malloc(sizeof(struct flCocoaRegion)); - R->count = 1; - R->rects = (CGRect *)malloc(sizeof(CGRect)); - *(R->rects) = Fl_Quartz_Graphics_Driver::fl_cgrectmake_cocoa(x, y, w, h); - return R; -} - -void Fl_Quartz_Graphics_Driver::XDestroyRegion(Fl_Region r_) { - if (r_) { - struct flCocoaRegion *r = (struct flCocoaRegion*)r_; - free(r->rects); - free(r); - } -} - -void Fl_Quartz_Graphics_Driver::cache_size(Fl_Image *img, int &width, int &height) { - width *= 2 * scale(); - height *= 2 * scale(); -} - -float Fl_Quartz_Graphics_Driver::override_scale() { - float s = scale(); - if (s != 1.f && Fl_Display_Device::display_device()->is_current()) { - CGContextScaleCTM(gc_, 1./s, 1./s); - Fl_Graphics_Driver::scale(1); - } - return s; -} - -void Fl_Quartz_Graphics_Driver::restore_scale(float s) { - if (s != 1.f && Fl_Display_Device::display_device()->is_current()) { - CGContextScaleCTM(gc_, s, s); - Fl_Graphics_Driver::scale(s); - } -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_arci.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_arci.cxx deleted file mode 100644 index 4163de685..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_arci.cxx +++ /dev/null @@ -1,65 +0,0 @@ -// -// Arc (integer) drawing functions for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "Fl_Quartz_Graphics_Driver.H" -#include - -/** - \file quartz_arci.cxx - \brief Utility functions for drawing circles using integers -*/ - -void Fl_Quartz_Graphics_Driver::arc(int x,int y,int w,int h,double a1,double a2) { - if (w <= 0 || h <= 0) return; - a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI; - float cx = x + 0.5f*w - 0.5f, cy = y + 0.5f*h - 0.5f; - CGContextSetShouldAntialias(gc_, true); - if (w!=h) { - CGContextSaveGState(gc_); - CGContextTranslateCTM(gc_, cx, cy); - CGContextScaleCTM(gc_, w-1.0f, h-1.0f); - CGContextAddArc(gc_, 0, 0, 0.5, a1, a2, 1); - CGContextRestoreGState(gc_); - } else { - float r = (w+h)*0.25f-0.5f; - CGContextAddArc(gc_, cx, cy, r, a1, a2, 1); - } - CGContextStrokePath(gc_); - CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::pie(int x,int y,int w,int h,double a1,double a2) { - if (w <= 0 || h <= 0) return; - a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI; - float cx = x + 0.5f*w - 0.5f, cy = y + 0.5f*h - 0.5f; - CGContextSetShouldAntialias(gc_, true); - if (w!=h) { - CGContextSaveGState(gc_); - CGContextTranslateCTM(gc_, cx, cy); - CGContextScaleCTM(gc_, w, h); - CGContextAddArc(gc_, 0, 0, 0.5, a1, a2, 1); - CGContextAddLineToPoint(gc_, 0, 0); - CGContextClosePath(gc_); - CGContextRestoreGState(gc_); - } else { - float r = (w+h)*0.25f; - CGContextAddArc(gc_, cx, cy, r, a1, a2, 1); - CGContextAddLineToPoint(gc_, cx, cy); - CGContextClosePath(gc_); - } - CGContextFillPath(gc_); - CGContextSetShouldAntialias(gc_, false); -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_color.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_color.cxx deleted file mode 100644 index 9d31f8573..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_color.cxx +++ /dev/null @@ -1,70 +0,0 @@ -// -// MacOS color functions for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// The fltk "colormap". This allows ui colors to be stored in 8-bit -// locations, and provides a level of indirection so that global color -// changes can be made. Not to be confused with the X colormap, which -// I try to hide completely. - -// matt: Neither Quartz nor Quickdraw support colormaps in this implementation -// matt: Quartz support done - -#include "Fl_Quartz_Graphics_Driver.H" - -#include -#include -#include -#include - -extern unsigned fl_cmap[256]; // defined in fl_color.cxx - -void Fl_Quartz_Graphics_Driver::color(Fl_Color i) { - Fl_Graphics_Driver::color(i); - uchar r, g, b; - float fa = 1.0f; - if (i & 0xFFFFFF00) { - // translate rgb colors into color index - r = i>>24; - g = i>>16; - b = i>> 8; - } else { - // translate index into rgb: - unsigned c = fl_cmap[i]; - c = c ^ 0x000000ff; // trick to restore the color's correct alpha value - r = c>>24; - g = c>>16; - b = c>> 8; - uchar a = c & 0xff; - //printf("i=%d rgb=%u,%u,%u a=%u\n",i,r,g,b,a); - fa = a/255.0f; - } - if (!gc_) return; // no context yet? We will assign the color later. - float fr = r/255.0f; - float fg = g/255.0f; - float fb = b/255.0f; - CGContextSetRGBFillColor(gc_, fr, fg, fb, fa); - CGContextSetRGBStrokeColor(gc_, fr, fg, fb, fa); -} - -void Fl_Quartz_Graphics_Driver::color(uchar r, uchar g, uchar b) { - Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) ); - float fr = r/255.0f; - float fg = g/255.0f; - float fb = b/255.0f; - if (!gc_) return; // no context yet? We will assign the color later. - CGContextSetRGBFillColor(gc_, fr, fg, fb, 1.0f); - CGContextSetRGBStrokeColor(gc_, fr, fg, fb, 1.0f); -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_font.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_font.cxx deleted file mode 100644 index d373c1768..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_font.cxx +++ /dev/null @@ -1,561 +0,0 @@ -// -// MacOS font selection routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "Fl_Quartz_Graphics_Driver.H" -#include "Fl_Font.H" -#include -#include -#include -#include // for fl_utf8toUtf16() -#include // fl_strdup() - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 -const NSUInteger kCTFontOrientationHorizontal = kCTFontHorizontalOrientation; -#endif - -Fl_Fontdesc* fl_fonts = NULL; - -static CGAffineTransform font_mx = { 1, 0, 0, -1, 0, 0 }; - -static int fl_free_font = FL_FREE_FONT; - - -static CFMutableDictionaryRef attributes = NULL; - -static Fl_Fontdesc built_in_table_PS[] = { // PostScript font names preferred when Mac OS ≥ 10.5 - {"ArialMT"}, - {"Arial-BoldMT"}, - {"Arial-ItalicMT"}, - {"Arial-BoldItalicMT"}, - {"Courier"}, - {"Courier-Bold"}, - {"Courier-Oblique"}, - {"Courier-BoldOblique"}, - {"TimesNewRomanPSMT"}, - {"TimesNewRomanPS-BoldMT"}, - {"TimesNewRomanPS-ItalicMT"}, - {"TimesNewRomanPS-BoldItalicMT"}, - {"Symbol"}, - {"Monaco"}, - {"AndaleMono"}, // there is no bold Monaco font on standard Mac - {"ZapfDingbatsITC"} -}; - - -// Bug: older versions calculated the value for *ap as a side effect of -// making the name, and then forgot about it. To avoid having to change -// the header files I decided to store this value in the last character -// of the font name array. -#define ENDOFBUFFER sizeof(fl_fonts->fontname)-1 - -// turn a stored font name into a pretty name: -const char* Fl_Quartz_Graphics_Driver::get_font_name(Fl_Font fnum, int* ap) { - if (!fl_fonts) fl_fonts = calc_fl_fonts(); - Fl_Fontdesc *f = fl_fonts + fnum; - if (!f->fontname[0]) { - this->set_fontname_in_fontdesc(f); - const char* thisFont = f->name; - if (!thisFont || !*thisFont) {if (ap) *ap = 0; return "";} - int type = 0; - if (strstr(f->name, "Bold")) type |= FL_BOLD; - if (strstr(f->name, "Italic") || strstr(f->name, "Oblique")) type |= FL_ITALIC; - f->fontname[ENDOFBUFFER] = (char)type; - } - if (ap) *ap = f->fontname[ENDOFBUFFER]; - return f->fontname; -} - - -int Fl_Quartz_Graphics_Driver::get_font_sizes(Fl_Font fnum, int*& sizep) { - static int array[128]; - if (!fl_fonts) fl_fonts = calc_fl_fonts(); - Fl_Fontdesc *s = fl_fonts+fnum; - if (!s->name) s = fl_fonts; // empty slot in table, use entry 0 - int cnt = 0; - - // ATS supports all font size - array[0] = 0; - sizep = array; - cnt = 1; - - return cnt; -} - -Fl_Quartz_Font_Descriptor::Fl_Quartz_Font_Descriptor(const char* name, Fl_Fontsize Size) : Fl_Font_Descriptor(name, Size) { - fontref = NULL; - Fl_Quartz_Graphics_Driver *driver = (Fl_Quartz_Graphics_Driver*)&Fl_Graphics_Driver::default_driver(); - driver->descriptor_init(name, size, this); -} - - -Fl_Quartz_Font_Descriptor::~Fl_Quartz_Font_Descriptor() { -/* -#if HAVE_GL - // ++ todo: remove OpenGL font allocations -// Delete list created by gl_draw(). This is not done by this code -// as it will link in GL unnecessarily. There should be some kind -// of "free" routine pointer, or a subclass? -#endif - */ - if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL); - if (fontref) { - CFRelease(fontref); - for (unsigned i = 0; i < sizeof(width)/sizeof(float*); i++) { - if (width[i]) free(width[i]); - } - } -} - - -static UniChar *utfWbuf = 0; -static unsigned utfWlen = 0; - -static UniChar *mac_Utf8_to_Utf16(const char *txt, int len, int *new_len) -{ - unsigned wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); - if (wlen >= utfWlen) - { - utfWlen = wlen + 100; - if (utfWbuf) free(utfWbuf); - utfWbuf = (UniChar*)malloc((utfWlen)*sizeof(UniChar)); - wlen = fl_utf8toUtf16(txt, len, (unsigned short*)utfWbuf, utfWlen); - } - *new_len = wlen; - return utfWbuf; -} - - -static Fl_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size) { - if (!fl_fonts) fl_fonts = Fl_Graphics_Driver::default_driver().calc_fl_fonts(); - Fl_Fontdesc* s = fl_fonts+fnum; - if (!s->name) s = fl_fonts; // use 0 if fnum undefined - Fl_Font_Descriptor* f; - for (f = s->first; f; f = f->next) - if (f->size == size) return f; - f = new Fl_Quartz_Font_Descriptor(s->name, size); - f->next = s->first; - s->first = f; - return f; -} - - -void Fl_Quartz_Graphics_Driver::font(Fl_Font fnum, Fl_Fontsize size) { - if (fnum == -1) { - Fl_Graphics_Driver::font(0, 0); - return; - } - Fl_Graphics_Driver::font(fnum, size); - font_descriptor( find(fnum, size) ); -} - -Fl_Quartz_Font_Descriptor *Fl_Quartz_Graphics_Driver::valid_font_descriptor() { - // avoid a crash if no font has been selected by user yet - if (!font_descriptor()) font(FL_HELVETICA, FL_NORMAL_SIZE); - return (Fl_Quartz_Font_Descriptor*)font_descriptor(); -} - -int Fl_Quartz_Graphics_Driver::height() { - Fl_Quartz_Font_Descriptor *fl_fontsize = valid_font_descriptor(); - return fl_fontsize->ascent + fl_fontsize->descent; -} - -int Fl_Quartz_Graphics_Driver::descent() { - Fl_Quartz_Font_Descriptor *fl_fontsize = valid_font_descriptor(); - return fl_fontsize->descent + 1; -} - -void Fl_Quartz_Graphics_Driver::draw(const char* str, int n, int x, int y) { - draw(str, n, (float)x, y+0.5f); -} - -void Fl_Quartz_Graphics_Driver::draw(int angle, const char *str, int n, int x, int y) { - CGContextSaveGState(gc_); - CGContextTranslateCTM(gc_, x, y); - CGContextRotateCTM(gc_, - angle*(M_PI/180) ); - draw(str, n, 0, 0); - CGContextRestoreGState(gc_); -} - -void Fl_Quartz_Graphics_Driver::rtl_draw(const char* c, int n, int x, int y) { - int dx, dy, w, h; - text_extents(c, n, dx, dy, w, h); - draw(c, n, x - w - dx, y); -} - -double Fl_Quartz_Graphics_Driver::width(const char* txt, int n) { - if (n == 0) return 0; - int len1 = fl_utf8len1(*txt); - if (len1 > 0 && n > len1) { // a text with several codepoints: compute its typographical width - CFStringRef str = CFStringCreateWithBytes(NULL, (const UInt8*)txt, n, kCFStringEncodingUTF8, false); - if (str) { - CFDictionarySetValue(attributes, kCTFontAttributeName, valid_font_descriptor()->fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str, attributes); - CFRelease(str); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - double d = CTLineGetTypographicBounds(ctline, NULL, NULL, NULL); - CFRelease(ctline); - return d; - } - } - int wc_len = n; - UniChar *uniStr = mac_Utf8_to_Utf16(txt, n, &wc_len); - return width(uniStr, wc_len); -} - -double Fl_Quartz_Graphics_Driver::width(unsigned int wc) { - UniChar utf16[3]; - int l = 1; - if (wc <= 0xFFFF) { - *utf16 = wc; - } - else { - l = (int)fl_ucs_to_Utf16(wc, utf16, 3); - } - return width(utf16, l); -} - -void Fl_Quartz_Graphics_Driver::set_fontname_in_fontdesc(Fl_Fontdesc *f) { - CFStringRef cfname = CFStringCreateWithCString(NULL, f->name, kCFStringEncodingUTF8); - CTFontRef ctfont = cfname ? CTFontCreateWithName(cfname, 0, NULL) : NULL; - if (cfname) { CFRelease(cfname); cfname = NULL; } - if (ctfont) { - cfname = CTFontCopyFullName(ctfont); - CFRelease(ctfont); - if (cfname) { - CFStringGetCString(cfname, f->fontname, ENDOFBUFFER, kCFStringEncodingUTF8); - CFRelease(cfname); - } - } - if (!cfname) strlcpy(f->fontname, f->name, ENDOFBUFFER); -} - -const char *Fl_Quartz_Graphics_Driver::font_name(int num) { - if (!fl_fonts) fl_fonts = calc_fl_fonts(); - return fl_fonts[num].name; -} - -void Fl_Quartz_Graphics_Driver::font_name(int num, const char *name) { - Fl_Fontdesc *s = fl_fonts + num; - if (s->name) { - if (!strcmp(s->name, name)) {s->name = name; return;} - for (Fl_Font_Descriptor* f = s->first; f;) { - Fl_Font_Descriptor* n = f->next; delete f; f = n; - } - s->first = 0; - } - s->name = name; - s->fontname[0] = 0; - s->first = 0; -} - - -Fl_Fontdesc* Fl_Quartz_Graphics_Driver::calc_fl_fonts(void) -{ - return built_in_table_PS; -} - - -void Fl_Quartz_Graphics_Driver::descriptor_init(const char* name, - Fl_Fontsize size, Fl_Quartz_Font_Descriptor *d) -{ - CFStringRef str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); - d->fontref = CTFontCreateWithName(str, size, NULL); - CGGlyph glyph[2]; - const UniChar A[2]={'W','.'}; - CTFontGetGlyphsForCharacters(d->fontref, A, glyph, 2); - CGSize advances[2]; - double w; - CTFontGetAdvancesForGlyphs(d->fontref, kCTFontOrientationHorizontal, glyph, advances, 2); - w = advances[0].width; - if ( fabs(advances[0].width - advances[1].width) < 1E-2 ) {//this is a fixed-width font - // slightly rescale fixed-width fonts so the character width has an integral value - CFRelease(d->fontref); - CGFloat fsize = size / ( w/floor(w + 0.5) ); - d->fontref = CTFontCreateWithName(str, fsize, NULL); - w = CTFontGetAdvancesForGlyphs(d->fontref, kCTFontOrientationHorizontal, glyph, NULL, 1); - } - CFRelease(str); - d->ascent = (short)(CTFontGetAscent(d->fontref) + 0.5); - d->descent = (short)(CTFontGetDescent(d->fontref) + 0.5); - d->q_width = w + 0.5; - for (unsigned i = 0; i < sizeof(d->width)/sizeof(float*); i++) d->width[i] = NULL; - if (!attributes) { - static CFNumberRef zero_ref; - float zero = 0.; - zero_ref = CFNumberCreate(NULL, kCFNumberFloat32Type, &zero); - // deactivate kerning for all fonts, so that string width = sum of character widths - // which allows fast fl_width() implementation. - attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, - 3, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFDictionarySetValue (attributes, kCTKernAttributeName, zero_ref); - } - if (d->ascent == 0) { // this may happen with some third party fonts - CFDictionarySetValue (attributes, kCTFontAttributeName, d->fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("Wj"), attributes); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - CGFloat fascent, fdescent; - CTLineGetTypographicBounds(ctline, &fascent, &fdescent, NULL); - CFRelease(ctline); - d->ascent = (short)(fascent + 0.5); - d->descent = (short)(fdescent + 0.5); - } -} - -// returns width of a pair of UniChar's in the surrogate range -static CGFloat surrogate_width(const UniChar *txt, Fl_Quartz_Font_Descriptor *fl_fontsize) -{ - CTFontRef font2 = fl_fontsize->fontref; - bool must_release = false; - CGGlyph glyphs[2]; - bool b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); - CGSize a; - if(!b) { // the current font doesn't contain this char - CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, txt, 2, kCFAllocatorNull); - // find a font that contains it - font2 = CTFontCreateForString(font2, str, CFRangeMake(0,2)); - must_release = true; - CFRelease(str); - b = CTFontGetGlyphsForCharacters(font2, txt, glyphs, 2); - } - if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontOrientationHorizontal, glyphs, &a, 1); - else a.width = fl_fontsize->q_width; - if(must_release) CFRelease(font2); - return a.width; -} - -static CGFloat variation_selector_width(CFStringRef str16, Fl_Quartz_Font_Descriptor *fl_fontsize) -{ - CGFloat retval; - CFDictionarySetValue(attributes, kCTFontAttributeName, fl_fontsize->fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - retval = CTLineGetOffsetForStringIndex(ctline, 2, NULL); - CFRelease(ctline); - return retval; -} - -double Fl_Quartz_Graphics_Driver::width(const UniChar* txt, int n) -{ - double retval = 0; - UniChar uni; - int i; - Fl_Quartz_Font_Descriptor *fl_fontsize = valid_font_descriptor(); - for (i = 0; i < n; i++) { // loop over txt - uni = txt[i]; - if (uni >= 0xD800 && uni <= 0xDBFF) { // handles the surrogate range - retval += surrogate_width(&txt[i], fl_fontsize); - i++; // because a pair of UniChar's represent a single character - continue; - } - if (i+1 < n && txt[i+1] >= 0xFE00 && txt[i+1] <= 0xFE0F) { // handles variation selectors - CFStringRef substr = CFStringCreateWithCharacters(NULL, txt + i, 2); - retval += variation_selector_width(substr, fl_fontsize); - CFRelease(substr); - i++; - continue; - } - const int block = 0x10000 / (sizeof(fl_fontsize->width)/sizeof(float*)); // block size - // r: index of the character block containing uni - unsigned int r = uni >> 7; // change 7 if sizeof(width) is changed - if (!fl_fontsize->width[r]) { // this character block has not been hit yet - //fprintf(stderr,"r=%d size=%d name=%s\n",r,fl_fontsize->size,fl_fonts[fl_font()].name); - // allocate memory to hold width of each character in the block - fl_fontsize->width[r] = (float*) malloc(sizeof(float) * block); - UniChar ii = r * block; - CGSize advance_size; - CGGlyph glyph; - for (int j = 0; j < block; j++) { // loop over the block - // ii spans all characters of this block - bool b = CTFontGetGlyphsForCharacters(fl_fontsize->fontref, &ii, &glyph, 1); - if (b) - CTFontGetAdvancesForGlyphs(fl_fontsize->fontref, kCTFontOrientationHorizontal, &glyph, &advance_size, 1); - else - advance_size.width = -1e9; // calculate this later - // the width of one character of this block of characters - fl_fontsize->width[r][j] = advance_size.width; - ii++; - } - } - // sum the widths of all characters of txt - double wdt = fl_fontsize->width[r][uni & (block-1)]; - if (wdt == -1e9) { - CGSize advance_size; - CGGlyph glyph; - CTFontRef font2 = fl_fontsize->fontref; - bool must_release = false; - bool b = CTFontGetGlyphsForCharacters(font2, &uni, &glyph, 1); - if (!b) { // the current font doesn't contain this char - CFStringRef str = CFStringCreateWithCharactersNoCopy(NULL, &uni, 1, kCFAllocatorNull); - // find a font that contains it - font2 = CTFontCreateForString(font2, str, CFRangeMake(0,1)); - must_release = true; - CFRelease(str); - b = CTFontGetGlyphsForCharacters(font2, &uni, &glyph, 1); - } - if (b) CTFontGetAdvancesForGlyphs(font2, kCTFontOrientationHorizontal, &glyph, &advance_size, 1); - else advance_size.width = 0.; - // the width of the 'uni' character - wdt = fl_fontsize->width[r][uni & (block-1)] = advance_size.width; - if (must_release) CFRelease(font2); - } - retval += wdt; - } - return retval; -} - - -// text extent calculation -void Fl_Quartz_Graphics_Driver::text_extents(const char *str8, int n, - int &dx, int &dy, int &w, int &h) { - Fl_Quartz_Font_Descriptor *fl_fontsize = valid_font_descriptor(); - UniChar *txt = mac_Utf8_to_Utf16(str8, n, &n); - CFStringRef str16 = CFStringCreateWithCharactersNoCopy(NULL, txt, n, kCFAllocatorNull); - CFDictionarySetValue (attributes, kCTFontAttributeName, fl_fontsize->fontref); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); - CFRelease(str16); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - CGContextSetTextPosition(gc_, 0, 0); - CGContextSetShouldAntialias(gc_, true); - CGRect rect = CTLineGetImageBounds(ctline, gc_); - CGContextSetShouldAntialias(gc_, false); - CFRelease(ctline); - dx = floor(rect.origin.x + 0.5); - dy = floor(- rect.origin.y - rect.size.height + 0.5); - w = rect.size.width + 0.5; - h = rect.size.height + 0.5; -} - - -static CGColorRef flcolortocgcolor(Fl_Color i) -{ - uchar r, g, b; - Fl::get_color(i, r, g, b); - CGFloat components[4] = {r/255.0f, g/255.0f, b/255.0f, 1.}; - static CGColorSpaceRef cspace = NULL; - if (cspace == NULL) { - cspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - } - return CGColorCreate(cspace, components); -} - -void Fl_Quartz_Graphics_Driver::draw(const char *str, int n, float x, float y) -{ - Fl_Quartz_Font_Descriptor *fl_fontsize = valid_font_descriptor(); - // convert to UTF-16 first - UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); - CGContextRef gc = (CGContextRef)this->gc(); - CFMutableStringRef str16 = CFStringCreateMutableWithExternalCharactersNoCopy(NULL, uniStr, n, n, kCFAllocatorNull); - if (str16 == NULL) return; // shd not happen - CGColorRef color = flcolortocgcolor(this->color()); - CFDictionarySetValue (attributes, kCTFontAttributeName, fl_fontsize->fontref); - CFDictionarySetValue (attributes, kCTForegroundColorAttributeName, color); - CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); - CFRelease(str16); - CFRelease(color); - CTLineRef ctline = CTLineCreateWithAttributedString(mastr); - CFRelease(mastr); - CGContextSetTextMatrix(gc, font_mx); - CGContextSetTextPosition(gc, x, y); - CGContextSetShouldAntialias(gc, true); - CTLineDraw(ctline, gc); - CGContextSetShouldAntialias(gc, false); - CFRelease(ctline); -} - -// Skip over bold/italic/oblique qualifiers part of PostScript font names -// Example: -// input: '-Regular_Light-Condensed' -// return: '_Light-Condensed' -// -static char *skip(char *p, int& derived) -{ - // 0 5 10 - // | | | - if (strncmp(p, "-BoldItalic", 11) == 0) { p += 11; derived = 3; } - else if (strncmp(p, "-BoldOblique", 12) == 0) { p += 12; derived = 3; } - else if (strncmp(p, "-Bold", 5) == 0) { p += 5; derived = 1; } - else if (strncmp(p, "-Italic", 7) == 0) { p += 7; derived = 2; } - else if (strncmp(p, "-Oblique", 8) == 0) { p += 8; derived = 2; } - else if (strncmp(p, "-Regular", 8) == 0) { p += 8; } - else if (strncmp(p, "-Roman", 6) == 0) { p += 6; } - return p; -} - -static int name_compare(const void *a, const void *b) -{ - /* Compare PostScript font names. - First compare font family names ignoring bold, italic and oblique qualifiers. - When families are identical, order them according to regular, bold, italic, bolditalic. - */ - char *n1 = *(char**)a; - char *n2 = *(char**)b; - int derived1 = 0; - int derived2 = 0; - while (true) { - if (*n1 == '-') n1 = skip(n1, derived1); - if (*n2 == '-') n2 = skip(n2, derived2); - if (*n1 < *n2) return -1; - if (*n1 > *n2) return +1; - if (*n1 == 0) { - return derived1 - derived2; - } - n1++; n2++; - } -} - -Fl_Font Fl_Quartz_Graphics_Driver::set_fonts(const char* xstarname) -{ -#pragma unused ( xstarname ) - if (fl_free_font > FL_FREE_FONT) return (Fl_Font)fl_free_font; // if already called - - int value[1] = {1}; - CFDictionaryRef dict = CFDictionaryCreate(NULL, - (const void **)kCTFontCollectionRemoveDuplicatesOption, - (const void **)&value, 1, NULL, NULL); - CTFontCollectionRef fcref = CTFontCollectionCreateFromAvailableFonts(dict); - CFRelease(dict); - CFArrayRef arrayref = CTFontCollectionCreateMatchingFontDescriptors(fcref); - CFRelease(fcref); - CFIndex count = CFArrayGetCount(arrayref); - CFIndex i; - char **tabfontnames = new char*[count]; - for (i = 0; i < count; i++) { - CTFontDescriptorRef fdesc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(arrayref, i); - CTFontRef font = CTFontCreateWithFontDescriptor(fdesc, 0., NULL); - CFStringRef cfname = CTFontCopyPostScriptName(font); - CFRelease(font); - CFDataRef cfdata = CFStringCreateExternalRepresentation(NULL, cfname, kCFStringEncodingUTF8, '?'); - CFIndex l = CFDataGetLength(cfdata); - tabfontnames[i] = (char*)malloc(l+1); // never free'ed - memcpy(tabfontnames[i], CFDataGetBytePtr(cfdata), l); - tabfontnames[i][l] = 0; - CFRelease(cfdata); - CFRelease(cfname); - } - CFRelease(arrayref); - qsort(tabfontnames, count, sizeof(char*), name_compare); - for (i = 0; i < count; i++) { - Fl::set_font((Fl_Font)(fl_free_font++), tabfontnames[i]); - } - delete[] tabfontnames; - return (Fl_Font)fl_free_font; -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_image.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_image.cxx deleted file mode 100644 index 932ef7d82..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_image.cxx +++ /dev/null @@ -1,284 +0,0 @@ -// -// MacOS image drawing code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include "Fl_Quartz_Graphics_Driver.H" -#include -#include -#include -#include - -#define MAXBUFFER 0x40000 // 256k - -static void dataReleaseCB(void *info, const void *data, size_t size) -{ - delete[] (uchar *)data; -} - -/* - draw an image based on the input parameters - - buf: image source data - X, Y: position (in buffer?!) - W, H: size of picture (in pixel?) - delta: distance from pixel to pixel in buf in bytes - linedelta: distance from line to line in buf in bytes - mono: if set, pixel is one byte - if zero, pixel is 3 byte - cb: callback to copy image data into (RGB?) buffer - buf: pointer to first byte in image source - x, y: position in buffer - w: width (in bytes?) - dst: destination buffer - userdata: ? - */ -static void innards(const uchar *buf, int X, int Y, int W, int H, - int delta, int linedelta, int mono, - Fl_Draw_Image_Cb cb, void* userdata, CGContextRef gc, Fl_Quartz_Graphics_Driver *driver) -{ - if (!linedelta) linedelta = W*abs(delta); - - uchar *tmpBuf = 0; - if (!cb) { - if (delta < 0) buf -= (W-1)*(-delta); - if (linedelta < 0) buf -= (H-1)*abs(linedelta); - } - const void *array = buf; - if (cb || driver->has_feature(Fl_Quartz_Graphics_Driver::PRINTER)) { - tmpBuf = new uchar[ H*W*abs(delta) ]; - if (cb) { - for (int i=0; idraw_CGImage(img, 0,0,W,H, 0,0,W,H); - CGImageRelease(img); - CGContextRestoreGState(gc); - } -} - -void Fl_Quartz_Graphics_Driver::draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l){ - d &= ~FL_IMAGE_WITH_ALPHA; - innards(buf,x,y,w,h,d,l,(d<3&&d>-3),0,0,gc_,this); -} -void Fl_Quartz_Graphics_Driver::draw_image(Fl_Draw_Image_Cb cb, void* data, - int x, int y, int w, int h,int d) { - innards(0,x,y,w,h,d,0,(d<3&&d>-3),cb,data,gc_,this); -} -void Fl_Quartz_Graphics_Driver::draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l){ - innards(buf,x,y,w,h,d,l,1,0,0,gc_,this); -} -void Fl_Quartz_Graphics_Driver::draw_image_mono(Fl_Draw_Image_Cb cb, void* data, - int x, int y, int w, int h,int d) { - innards(0,x,y,w,h,d,0,1,cb,data,gc_,this); -} - - -void Fl_Quartz_Graphics_Driver::draw_bitmap(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy) { - int X, Y, W, H; - if (!bm->array) { - draw_empty(bm, XP, YP); - return; - } - if (start_image(bm, XP,YP,WP,HP,cx,cy,X,Y,W,H)) return; - if (!*id(bm)) - cache(bm); - - if (*Fl_Graphics_Driver::id(bm) && gc_) { - draw_CGImage((CGImageRef)*Fl_Graphics_Driver::id(bm), X,Y,W,H, cx, cy, bm->w(), bm->h()); - } -} - -void Fl_Quartz_Graphics_Driver::cache(Fl_RGB_Image *rgb) { - CGColorSpaceRef lut = rgb->d()<=2 ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); - int ld = rgb->ld(); - if (!ld) ld = rgb->data_w() * rgb->d(); - CGDataProviderRef src; - if ( has_feature(PRINTER) ) { - // When printing or copying to clipboard, the data at rgb->array are used when - // the PDF page is completed, that is, after return from this function. - // At that stage, the rgb object has possibly been deleted. It is therefore necessary - // to use a copy of rgb->array. The mask_ member of rgb - // is used to avoid repeating the copy operation if rgb is drawn again. - // The CGImage data provider deletes the copy at the latest of these two events: - // deletion of rgb, and completion of the PDF page where rgb was drawn. - size_t total = ld * rgb->data_h(); - uchar *copy = new uchar[total]; - memcpy(copy, rgb->array, total); - src = CGDataProviderCreateWithData(NULL, copy, total, dataReleaseCB); - *Fl_Graphics_Driver::mask(rgb) = 1; - } else { - // the CGImage data provider must not release the image data. - src = CGDataProviderCreateWithData(NULL, rgb->array, ld * rgb->data_h(), NULL); - } - CGImageRef cgimg = CGImageCreate(rgb->data_w(), rgb->data_h(), 8, rgb->d()*8, ld, - lut, (rgb->d()&1)?kCGImageAlphaNone:kCGImageAlphaLast, - src, 0L, false, kCGRenderingIntentDefault); - *Fl_Graphics_Driver::id(rgb) = (fl_uintptr_t)cgimg; - CGColorSpaceRelease(lut); - CGDataProviderRelease(src); -} - -void Fl_Quartz_Graphics_Driver::draw_rgb(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) { - int X, Y, W, H; - // Don't draw an empty image... - if (!img->d() || !img->array) { - Fl_Graphics_Driver::draw_empty(img, XP, YP); - return; - } - if (start_image(img, XP, YP, WP, HP, cx, cy, X, Y, W, H)) { - return; - } - CGImageRef cgimg = (CGImageRef)*Fl_Graphics_Driver::id(img); - if (cgimg && has_feature(PRINTER) && !*Fl_Graphics_Driver::mask(img)) { - CGImageRelease(cgimg); - *Fl_Graphics_Driver::id(img) = 0; - cgimg = NULL; - } - if (!cgimg) { - cache(img); - cgimg = (CGImageRef)*Fl_Graphics_Driver::id(img); - } - if (cgimg && gc_) { - draw_CGImage(cgimg, X,Y,W,H, cx,cy, img->w(), img->h()); - } -} - -void Fl_Quartz_Graphics_Driver::draw_pixmap(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) { - int X, Y, W, H; - if (!pxm->data() || !pxm->w()) { - draw_empty(pxm, XP, YP); - return; - } - if ( start_image(pxm, XP,YP,WP,HP,cx,cy,X,Y,W,H) ) return; - if (!*id(pxm)) { - cache(pxm); - } - - CGImageRef cgimg = (CGImageRef)*Fl_Graphics_Driver::id(pxm); - draw_CGImage(cgimg, X,Y,W,H, cx,cy, pxm->w(), pxm->h()); -} - -CGImageRef Fl_Quartz_Graphics_Driver::create_bitmask(int w, int h, const uchar *array) { - static uchar reverse[16] = /* Bit reversal lookup table */ - { 0x00, 0x88, 0x44, 0xcc, 0x22, 0xaa, 0x66, 0xee, - 0x11, 0x99, 0x55, 0xdd, 0x33, 0xbb, 0x77, 0xff }; - int rowBytes = (w+7)>>3 ; - uchar *bmask = new uchar[rowBytes*h]; - uchar *dst = bmask; - const uchar *src = array; - for ( int i=rowBytes*h; i>0; i--,src++ ) { - *dst++ = ((reverse[*src & 0x0f] & 0xf0) | (reverse[(*src >> 4) & 0x0f] & 0x0f))^0xff; - } - CGDataProviderRef srcp = CGDataProviderCreateWithData( NULL, bmask, rowBytes*h, dataReleaseCB); - CGImageRef id_ = CGImageMaskCreate( w, h, 1, 1, rowBytes, srcp, 0L, false); - CGDataProviderRelease(srcp); - return id_; -} - -void Fl_Quartz_Graphics_Driver::delete_bitmask(fl_uintptr_t bm) { - if (bm) CGImageRelease((CGImageRef)bm); -} - -void Fl_Quartz_Graphics_Driver::uncache(Fl_RGB_Image*, fl_uintptr_t &id_, fl_uintptr_t &mask_) { - if (id_) { - CGImageRelease((CGImageRef)id_); - id_ = 0; - mask_ = 0; - } -} - -void Fl_Quartz_Graphics_Driver::cache(Fl_Bitmap *bm) { - *Fl_Graphics_Driver::id(bm) = (fl_uintptr_t)create_bitmask(bm->data_w(), bm->data_h(), bm->array); -} - - -static void pmProviderRelease (void *ctxt, const void *data, size_t size) { - CFRelease(ctxt); -} - -void Fl_Quartz_Graphics_Driver::cache(Fl_Pixmap *img) { - Fl_Image_Surface *surf = new Fl_Image_Surface(img->data_w(), img->data_h()); - Fl_Surface_Device::push_current(surf); - fl_draw_pixmap(img->data(), 0, 0, FL_BLACK); - Fl_Surface_Device::pop_current(); - CGContextRef src = (CGContextRef)Fl_Graphics_Driver::get_offscreen_and_delete_image_surface(surf); - void *cgdata = CGBitmapContextGetData(src); - int sw = (int)CGBitmapContextGetWidth(src); - int sh = (int)CGBitmapContextGetHeight(src); - CGImageAlphaInfo alpha = CGBitmapContextGetAlphaInfo(src); - CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); - CGDataProviderRef src_bytes = CGDataProviderCreateWithData(src, cgdata, sw*sh*4, pmProviderRelease); - CGImageRef cgimg = CGImageCreate( sw, sh, 8, 4*8, 4*sw, lut, alpha, - src_bytes, 0L, false, kCGRenderingIntentDefault); - CGColorSpaceRelease(lut); - CGDataProviderRelease(src_bytes); - *Fl_Graphics_Driver::id(img) = (fl_uintptr_t)cgimg; -} - -void Fl_Quartz_Graphics_Driver::draw_CGImage(CGImageRef cgimg, int x, int y, int w, int h, int srcx, int srcy, int sw, int sh) -{ - CGRect rect = CGRectMake(x, y, w, h); - CGContextSaveGState(gc_); - CGContextClipToRect(gc_, CGRectOffset(rect, -0.5, -0.5 )); - // move graphics context to origin of vertically reversed image - // The 0.5 here cancels the 0.5 offset present in Quartz graphics contexts. - // Thus, image and surface pixels are in phase. - CGContextTranslateCTM(gc_, rect.origin.x - srcx - 0.5, rect.origin.y - srcy + sh - 0.5); - CGContextScaleCTM(gc_, 1, -1); - CGContextDrawImage(gc_, CGRectMake(0, 0, sw, sh), cgimg); - CGContextRestoreGState(gc_); -} - -void Fl_Quartz_Graphics_Driver::uncache_pixmap(fl_uintptr_t pixmap_ref) { - CGImageRelease((CGImageRef)pixmap_ref); -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_line_style.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_line_style.cxx deleted file mode 100644 index 6a63ae483..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_line_style.cxx +++ /dev/null @@ -1,83 +0,0 @@ -// -// Line style code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include - - -/** - \file quartz_line_style.cxx - \brief Line style drawing utility hiding different platforms. -*/ - -#include "Fl_Quartz_Graphics_Driver.H" - -void Fl_Quartz_Graphics_Driver::quartz_restore_line_style() { - CGContextSetLineWidth(gc_, quartz_line_width_); - CGContextSetLineCap(gc_, quartz_line_cap_); - CGContextSetLineJoin(gc_, quartz_line_join_); - CGContextSetLineDash(gc_, 0, quartz_line_pattern, quartz_line_pattern_size); -} - -void Fl_Quartz_Graphics_Driver::line_style(int style, int width, char* dashes) { - - static CGLineCap Cap[4] = { kCGLineCapButt, kCGLineCapButt, - kCGLineCapRound, kCGLineCapSquare }; - static CGLineJoin Join[4] = { kCGLineJoinMiter, kCGLineJoinMiter, - kCGLineJoinRound, kCGLineJoinBevel }; - if (width<1) width = 1; - quartz_line_width_ = (float)width; - quartz_line_cap_ = Cap[(style>>8)&3]; - // when printing kCGLineCapSquare seems better for solid lines - if ( Fl_Surface_Device::surface() != Fl_Display_Device::display_device() - && style == FL_SOLID && dashes == NULL ) - { - quartz_line_cap_ = kCGLineCapSquare; - } - quartz_line_join_ = Join[(style>>12)&3]; - char *d = dashes; - static CGFloat pattern[16]; - if (d && *d) { - CGFloat *pDst = pattern; - while (*d) { *pDst++ = (float)*d++; } - quartz_line_pattern = pattern; - quartz_line_pattern_size = (int)(d-dashes); - } else if (style & 0xff) { - char dash, dot, gap; - // adjust lengths to account for cap: - if (style & 0x200) { - dash = char(2*width); - dot = 1; - gap = char(2*width-1); - } else { - dash = char(3*width); - dot = gap = char(width); - } - CGFloat *pDst = pattern; - switch (style & 0xff) { - case FL_DASH: *pDst++ = dash; *pDst++ = gap; break; - case FL_DOT: *pDst++ = dot; *pDst++ = gap; break; - case FL_DASHDOT: *pDst++ = dash; *pDst++ = gap; *pDst++ = dot; *pDst++ = gap; break; - case FL_DASHDOTDOT: *pDst++ = dash; *pDst++ = gap; *pDst++ = dot; *pDst++ = gap; *pDst++ = dot; *pDst++ = gap; break; - } - quartz_line_pattern_size = (int)(pDst-pattern); - quartz_line_pattern = pattern; - } else { - quartz_line_pattern = 0; - quartz_line_pattern_size = 0; - } - quartz_restore_line_style(); -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_rect.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_rect.cxx deleted file mode 100644 index 234853ad6..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_rect.cxx +++ /dev/null @@ -1,310 +0,0 @@ -// -// Rectangle drawing routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2018 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include -#include -#include - - -/** - \file quartz_rect.cxx - \brief Apple Quartz specific line and polygon drawing with integer coordinates. -*/ - - -#include "Fl_Quartz_Graphics_Driver.H" - - -// --- line and polygon drawing with integer coordinates - -void Fl_Quartz_Graphics_Driver::point(int x, int y) { - CGContextFillRect(gc_, CGRectMake(x - 0.5, y - 0.5, 1, 1) ); -} - -void Fl_Quartz_Graphics_Driver::rect(int x, int y, int w, int h) { - if (w<=0 || h<=0) return; - double offset = (quartz_line_width_ >= 2 ? quartz_line_width_/4 : 0); - CGRect rect = CGRectMake(x - offset, y - offset, w-1, h-1); - CGContextStrokeRect(gc_, rect); -} - -void Fl_Quartz_Graphics_Driver::focus_rect(int x, int y, int w, int h) -{ - CGContextSaveGState(gc_); - float s = scale(); - CGContextScaleCTM(gc_, 1/s, 1/s); - CGFloat lw = (s >= 1 ? floor(s) : 1); - CGContextSetLineWidth(gc_, lw); - CGFloat dots[2] = {lw, lw}; - CGContextSetLineDash(gc_, 0, dots, 2); - CGContextStrokeRect(gc_, CGRectMake(x*s, y*s, (w-1)*s, (h-1)*s)); - CGContextRestoreGState(gc_); -} - -void Fl_Quartz_Graphics_Driver::rectf(int x, int y, int w, int h) { - if (w<=0 || h<=0) return; - CGRect rect = CGRectMake(x - 0.5, y - 0.5, w , h); - CGContextFillRect(gc_, rect); -} - -void Fl_Quartz_Graphics_Driver::line(int x, int y, int x1, int y1) { - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y1); - CGContextStrokePath(gc_); - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::line(int x, int y, int x1, int y1, int x2, int y2) { - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y1); - CGContextAddLineToPoint(gc_, x2, y2); - CGContextStrokePath(gc_); - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::xyline(int x, int y, int x1) { - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y); - CGContextStrokePath(gc_); - if (high_resolution() || scale()>=2) { - /* On retina displays, all xyline() and yxline() functions produce lines that are half-unit - (or one pixel) too short at both ends. This is corrected by filling at both ends rectangles - of size one unit by line-width. - */ - CGContextFillRect(gc_, CGRectMake(x-0.5 , y - quartz_line_width_/2, 1 , quartz_line_width_)); - CGContextFillRect(gc_, CGRectMake(x1-0.5 , y - quartz_line_width_/2, 1 , quartz_line_width_)); - } - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::xyline(int x, int y, int x1, int y2) { - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y); - CGContextAddLineToPoint(gc_, x1, y2); - CGContextStrokePath(gc_); - if (high_resolution() || scale()>=2) { - CGContextFillRect(gc_, CGRectMake(x-0.5, y - quartz_line_width_/2, 1 , quartz_line_width_)); - CGContextFillRect(gc_, CGRectMake(x1 - quartz_line_width_/2, y2-0.5, quartz_line_width_, 1)); - } - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) { - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y); - CGContextAddLineToPoint(gc_, x1, y2); - CGContextAddLineToPoint(gc_, x3, y2); - CGContextStrokePath(gc_); - if (high_resolution() || scale()>=2) { - CGContextFillRect(gc_, CGRectMake(x-0.5, y - quartz_line_width_/2, 1 , quartz_line_width_)); - CGContextFillRect(gc_, CGRectMake(x3-0.5, y2 - quartz_line_width_/2, 1 , quartz_line_width_)); - } - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::yxline(int x, int y, int y1) { - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x, y1); - CGContextStrokePath(gc_); - if (high_resolution() || scale()>=2) { - CGContextFillRect(gc_, CGRectMake(x - quartz_line_width_/2, y-0.5, quartz_line_width_, 1)); - CGContextFillRect(gc_, CGRectMake(x - quartz_line_width_/2, y1-0.5, quartz_line_width_, 1)); - } - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::yxline(int x, int y, int y1, int x2) { - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x, y1); - CGContextAddLineToPoint(gc_, x2, y1); - CGContextStrokePath(gc_); - if (high_resolution() || scale()>=2) { - CGContextFillRect(gc_, CGRectMake(x - quartz_line_width_/2, y-0.5, quartz_line_width_, 1)); - CGContextFillRect(gc_, CGRectMake(x2-0.5, y1 - quartz_line_width_/2, 1 , quartz_line_width_)); - } - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3) { - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x, y1); - CGContextAddLineToPoint(gc_, x2, y1); - CGContextAddLineToPoint(gc_, x2, y3); - CGContextStrokePath(gc_); - if (high_resolution() || scale()>=2) { - CGContextFillRect(gc_, CGRectMake(x - quartz_line_width_/2, y-0.5, quartz_line_width_, 1)); - CGContextFillRect(gc_, CGRectMake(x2 - quartz_line_width_/2, y3-0.5, quartz_line_width_, 1)); - } - if (has_feature(PRINTER) || quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::loop(int x, int y, int x1, int y1, int x2, int y2) { - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y1); - CGContextAddLineToPoint(gc_, x2, y2); - CGContextClosePath(gc_); - CGContextStrokePath(gc_); - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::loop(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y1); - CGContextAddLineToPoint(gc_, x2, y2); - CGContextAddLineToPoint(gc_, x3, y3); - CGContextClosePath(gc_); - CGContextStrokePath(gc_); - if (quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::overlay_rect(int x, int y, int w , int h) { - float s = scale(); if (s < 2) s = 0; - CGContextSetLineWidth(gc_, 0.05); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x+w-1 +s/8, y); - CGContextAddLineToPoint(gc_, x+w-1 +s/8, y+h-1 -s/8); - CGContextAddLineToPoint(gc_, x, y+h-1 -s/8); - CGContextClosePath(gc_); - CGContextStrokePath(gc_); - CGContextSetLineWidth(gc_, quartz_line_width_); -} - -void Fl_Quartz_Graphics_Driver::polygon(int x, int y, int x1, int y1, int x2, int y2) { - CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y1); - CGContextAddLineToPoint(gc_, x2, y2); - CGContextClosePath(gc_); - CGContextFillPath(gc_); - CGContextSetShouldAntialias(gc_, false); -} - -void Fl_Quartz_Graphics_Driver::polygon(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { - CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, x, y); - CGContextAddLineToPoint(gc_, x1, y1); - CGContextAddLineToPoint(gc_, x2, y2); - CGContextAddLineToPoint(gc_, x3, y3); - CGContextClosePath(gc_); - CGContextFillPath(gc_); - CGContextSetShouldAntialias(gc_, false); -} - -// --- clipping - -// intersects current and x,y,w,h rectangle and returns result as a new Fl_Region -static Fl_Region intersect_region_and_rect(Fl_Region current_, int x,int y,int w, int h) -{ - if (current_ == NULL) return Fl_Graphics_Driver::default_driver().XRectangleRegion(x,y,w,h); - struct flCocoaRegion* current = (struct flCocoaRegion*)current_; - CGRect r = Fl_Quartz_Graphics_Driver::fl_cgrectmake_cocoa(x, y, w, h); - struct flCocoaRegion* outr = (struct flCocoaRegion*)malloc(sizeof(struct flCocoaRegion)); - outr->count = current->count; - outr->rects =(CGRect*)malloc(outr->count * sizeof(CGRect)); - int j = 0; - for(int i = 0; i < current->count; i++) { - CGRect test = CGRectIntersection(current->rects[i], r); - if (!CGRectIsEmpty(test)) outr->rects[j++] = test; - } - if (j) { - outr->count = j; - outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); - } - else { - Fl_Graphics_Driver::default_driver().XDestroyRegion(outr); - outr = (struct flCocoaRegion*)Fl_Graphics_Driver::default_driver().XRectangleRegion(0,0,0,0); - } - return outr; -} - - -void Fl_Quartz_Graphics_Driver::push_clip(int x, int y, int w, int h) { - Fl_Region r; - if (w > 0 && h > 0) { - r = XRectangleRegion(x,y,w,h); - Fl_Region current = rstack[rstackptr]; - if (current) { - XDestroyRegion(r); - r = intersect_region_and_rect(current, x,y,w,h); - } - } else { // make empty clip region: - r = XRectangleRegion(0,0,0,0); - } - if (rstackptr < region_stack_max) rstack[++rstackptr] = r; - else Fl::warning("Fl_Quartz_Graphics_Driver::push_clip: clip stack overflow!\n"); - restore_clip(); -} - -int Fl_Quartz_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){ - X = x; Y = y; W = w; H = h; - struct flCocoaRegion* r = (struct flCocoaRegion*)rstack[rstackptr]; - if (!r) return 0; - CGRect arg = fl_cgrectmake_cocoa(x, y, w, h); - CGRect u = CGRectMake(0,0,0,0); - CGRect test; - for (int i = 0; i < r->count; i++) { - test = CGRectIntersection(r->rects[i], arg); - if ( !CGRectIsEmpty(test) ) { - if(CGRectIsEmpty(u)) u = test; - else u = CGRectUnion(u, test); - } - } - X = int(u.origin.x + 0.5); // reverse offset introduced by fl_cgrectmake_cocoa() - Y = int(u.origin.y + 0.5); - W = int(u.size.width + 0.5); // round to nearest integer - H = int(u.size.height + 0.5); - if (CGRectIsEmpty(u)) W = H = 0; - return !CGRectEqualToRect(arg, u); -} - -int Fl_Quartz_Graphics_Driver::not_clipped(int x, int y, int w, int h) { - if (x+w <= 0 || y+h <= 0) return 0; - struct flCocoaRegion* r = (struct flCocoaRegion*)rstack[rstackptr]; - if (!r) return 1; - CGRect arg = fl_cgrectmake_cocoa(x, y, w, h); - for (int i = 0; i < r->count; i++) { - CGRect test = CGRectIntersection(r->rects[i], arg); - if (!CGRectIsEmpty(test)) return 1; - } - return 0; -} - -void Fl_Quartz_Graphics_Driver::restore_clip() { - fl_clip_state_number++; - struct flCocoaRegion* r = (struct flCocoaRegion*)rstack[rstackptr]; - if ( fl_window || gc_ ) { // clipping for a true window or an offscreen buffer - if (gc_) { - CGContextRestoreGState(gc_); - CGContextSaveGState(gc_); - } - color(color()); - quartz_restore_line_style(); - if (r) { //apply program clip - CGContextClipToRects(gc_, r->rects, r->count); - } - } -} diff --git a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_vertex.cxx b/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_vertex.cxx deleted file mode 100644 index 16c6c6c29..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Graphics_Driver_vertex.cxx +++ /dev/null @@ -1,98 +0,0 @@ -// -// Portable drawing routines for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file quartz_vertex.cxx - \brief Portable drawing code for drawing arbitrary shapes with - simple 2D transformations, implemented for OS X Quartz. -*/ - -#include "Fl_Quartz_Graphics_Driver.H" - -#include -#include -#include - - -void Fl_Quartz_Graphics_Driver::end_points() { - for (int i = 0; i < n; i++) { - point(xpoint[i].x, xpoint[i].y); - } -} - -void Fl_Quartz_Graphics_Driver::end_line() { - if (n < 2) { - end_points(); - return; - } - if (n<=1) return; - CGContextSetShouldAntialias(gc_, true); - CGContextMoveToPoint(gc_, xpoint[0].x, xpoint[0].y); - for (int i=1; i -#include - -class Fl_Quartz_Image_Surface_Driver : public Fl_Image_Surface_Driver { -private: - CGImageRef mask_; - void mask(const Fl_RGB_Image *) FL_OVERRIDE; - void end_current() FL_OVERRIDE; -public: - FLWindow *pre_window; - Fl_Quartz_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off); - ~Fl_Quartz_Image_Surface_Driver(); - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; - Fl_RGB_Image *image() FL_OVERRIDE; -}; - -#endif // FL_QUARTZ_IMAGE_SURFACE_DRIVER_H diff --git a/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx b/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx deleted file mode 100644 index 4615ba475..000000000 --- a/src/drivers/Quartz/Fl_Quartz_Image_Surface_Driver.cxx +++ /dev/null @@ -1,167 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include -#include "Fl_Quartz_Image_Surface_Driver.H" -#include "Fl_Quartz_Graphics_Driver.H" -#include "../Cocoa/Fl_Cocoa_Window_Driver.H" -#include - - -Fl_Quartz_Image_Surface_Driver::Fl_Quartz_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) { - mask_ = NULL; - int W = w, H = h; - float s = 1; - if (high_res) { - s = Fl_Graphics_Driver::default_driver().scale(); - Fl_Window *cw = Fl::first_window(); - Fl_Cocoa_Window_Driver *dr = cw ? Fl_Cocoa_Window_Driver::driver(cw) : NULL; - if (dr && dr->mapped_to_retina()) s *= 2; - W *= s; H *= s; - } - CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); - offscreen = off ? off : (Fl_Offscreen)CGBitmapContextCreate(calloc(W*H,4), W, H, 8, W*4, lut, kCGImageAlphaPremultipliedLast); - CGColorSpaceRelease(lut); - driver(new Fl_Quartz_Graphics_Driver); - CGContextTranslateCTM((CGContextRef)offscreen, 0.5*s, -0.5*s); // as when drawing to a window - if (high_res) { - CGContextScaleCTM((CGContextRef)offscreen, s, s); - driver()->scale(s); - } - CGContextSetShouldAntialias((CGContextRef)offscreen, false); - CGContextTranslateCTM((CGContextRef)offscreen, 0, height); - CGContextScaleCTM((CGContextRef)offscreen, 1.0f, -1.0f); - CGContextSaveGState((CGContextRef)offscreen); - CGContextSetRGBFillColor((CGContextRef)offscreen, 1, 1, 1, 0); - CGContextFillRect((CGContextRef)offscreen, CGRectMake(0,0,w,h)); -} - -Fl_Quartz_Image_Surface_Driver::~Fl_Quartz_Image_Surface_Driver() { - if (mask_) { - CGImageRelease(mask_); - } - if (offscreen) CGContextRestoreGState((CGContextRef)offscreen); - if (offscreen && !external_offscreen) { - void *data = CGBitmapContextGetData((CGContextRef)offscreen); - free(data); - CGContextRelease((CGContextRef)offscreen); - } - delete driver(); -} - - -void Fl_Quartz_Image_Surface_Driver::set_current() { - Fl_Surface_Device::set_current(); - pre_window = fl_window; - driver()->gc((CGContextRef)offscreen); - fl_window = 0; - ((Fl_Quartz_Graphics_Driver*)driver())->high_resolution( CGBitmapContextGetWidth((CGContextRef)offscreen) > (size_t)width ); - if (mask_) { - int W, H; - printable_rect(&W, &H); - CGContextSaveGState((CGContextRef)offscreen); - CGContextClipToMask((CGContextRef)offscreen, CGRectMake(0,0,W,H), mask_); // 10.4 - CGContextSaveGState((CGContextRef)offscreen); - } -} - -void Fl_Quartz_Image_Surface_Driver::translate(int x, int y) { - CGContextRestoreGState((CGContextRef)offscreen); - CGContextSaveGState((CGContextRef)offscreen); - CGContextTranslateCTM((CGContextRef)offscreen, x, y); - CGContextSaveGState((CGContextRef)offscreen); -} - -void Fl_Quartz_Image_Surface_Driver::untranslate() { - CGContextRestoreGState((CGContextRef)offscreen); -} - -Fl_RGB_Image* Fl_Quartz_Image_Surface_Driver::image() -{ - CGContextFlush((CGContextRef)offscreen); - if (mask_) { - CGContextRestoreGState((CGContextRef)offscreen); - CGImageRelease(mask_); - mask_ = NULL; - } - int W = (int)CGBitmapContextGetWidth((CGContextRef)offscreen); - int H = (int)CGBitmapContextGetHeight((CGContextRef)offscreen); - int bpr = (int)CGBitmapContextGetBytesPerRow((CGContextRef)offscreen); - int bpp = (int)CGBitmapContextGetBitsPerPixel((CGContextRef)offscreen)/8; - uchar *base = (uchar*)CGBitmapContextGetData((CGContextRef)offscreen); - int idx, idy; - uchar *pdst, *psrc; - unsigned char *data = new uchar[W * H * 3]; - for (idy = 0, pdst = data; idy < H; idy ++) { - for (idx = 0, psrc = base + idy * bpr; idx < W; idx ++, psrc += bpp, pdst += 3) { - pdst[0] = psrc[0]; // R - pdst[1] = psrc[1]; // G - pdst[2] = psrc[2]; // B - } - } - Fl_RGB_Image *image = new Fl_RGB_Image(data, W, H); - image->alloc_array = 1; - return image; -} - -void Fl_Quartz_Image_Surface_Driver::end_current() -{ - if (mask_) { - CGContextRestoreGState((CGContextRef)offscreen); - CGContextRestoreGState((CGContextRef)offscreen); - } - fl_window = pre_window; - Fl_Surface_Device::end_current(); -} - - -static void MyProviderReleaseData (void *info, const void *data, size_t size) { - delete[] (uchar*)data; -} - - -void Fl_Quartz_Image_Surface_Driver::mask(const Fl_RGB_Image *img) { - if (!&CGContextClipToMask) return; - int W = (int)CGBitmapContextGetWidth((CGContextRef)offscreen); - int H = (int)CGBitmapContextGetHeight((CGContextRef)offscreen); - bool using_copy = false; - if (W != img->data_w() || H != img->data_h()) { - Fl_RGB_Image *copy = (Fl_RGB_Image*)img->copy(W, H); - img = copy; - using_copy = true; - } - - int i, d = img->d(), w = img->data_w(), h = img->data_h(); - // reverse top and bottom and convert to gray scale if img->d() == 3 and complement bits - int bytes_per_row = (img->ld() ? img->ld() : w * d); - uchar *from = new uchar[w * h]; - for ( i = 0; i < h; i++) { - const uchar *p = img->array + bytes_per_row * i; - const uchar *last = p + bytes_per_row; - uchar *q = from + (h - 1 - i) * w; - while (p < last) { - unsigned u = *p++; - u += *p++; - u += *p++; - *q++ = ~(u/3); - } - } - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, w * h, MyProviderReleaseData); - mask_ = CGImageMaskCreate(w, h, 8, 8, w, provider, NULL, false); - CFRelease(provider); - if (using_copy) delete img; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H b/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H deleted file mode 100644 index e10a801ce..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.H +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef FL_WAYLAND_COPY_SURFACE_DRIVER_H -#define FL_WAYLAND_COPY_SURFACE_DRIVER_H - -#include -#include - -class Fl_Wayland_Copy_Surface_Driver : public Fl_Copy_Surface_Driver { - friend class Fl_Copy_Surface_Driver; - Fl_Image_Surface *img_surf; -protected: - Fl_Wayland_Copy_Surface_Driver(int w, int h); - ~Fl_Wayland_Copy_Surface_Driver(); - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; -}; - -#endif // FL_WAYLAND_COPY_SURFACE_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx deleted file mode 100644 index 043114781..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Copy_Surface_Driver.cxx +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copy-to-clipboard code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "Fl_Wayland_Copy_Surface_Driver.H" -#include -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Window_Driver.H" - - -Fl_Wayland_Copy_Surface_Driver::Fl_Wayland_Copy_Surface_Driver(int w, int h) : Fl_Copy_Surface_Driver(w, h) { - float os_scale = Fl_Graphics_Driver::default_driver().scale(); - int d = 1; - if (Fl::first_window()) { - d = Fl_Wayland_Window_Driver::driver(Fl::first_window())->wld_scale(); - } - img_surf = new Fl_Image_Surface(int(w * os_scale) * d, int(h * os_scale) * d); - driver(img_surf->driver()); - driver()->scale(d * os_scale); -} - - -Fl_Wayland_Copy_Surface_Driver::~Fl_Wayland_Copy_Surface_Driver() { - Fl_RGB_Image *rgb = img_surf->image(); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - scr_driver->copy_image(rgb->array, rgb->data_w(), rgb->data_h()); - delete rgb; - delete img_surf; - driver(NULL); -} - - -void Fl_Wayland_Copy_Surface_Driver::set_current() { - Fl_Surface_Device::set_current(); - Fl_Cairo_Graphics_Driver *dr = (Fl_Cairo_Graphics_Driver*)driver(); - if (!dr->cr()) dr->set_cairo((cairo_t*)img_surf->offscreen()); -} - - -void Fl_Wayland_Copy_Surface_Driver::translate(int x, int y) { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_translate(x, y); -} - - -void Fl_Wayland_Copy_Surface_Driver::untranslate() { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_untranslate(); -} diff --git a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H deleted file mode 100644 index ad67c01bf..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.H +++ /dev/null @@ -1,62 +0,0 @@ -// -// Class Fl_Wayland_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef FL_WAYLAND_GL_WINDOW_DRIVER_H -#define FL_WAYLAND_GL_WINDOW_DRIVER_H - -#include -#if HAVE_GL -#include "../../Fl_Gl_Window_Driver.H" -#include -#include -#include - - -class Fl_Wayland_Gl_Window_Driver : public Fl_Gl_Window_Driver { - friend Fl_Gl_Window_Driver* Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *); - friend class Fl_Wayland_Gl_Plugin; -private: - static EGLDisplay egl_display; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; - bool need_swap; - Fl_Wayland_Gl_Window_Driver(Fl_Gl_Window *win); - float pixels_per_unit() FL_OVERRIDE; - void make_current_before() FL_OVERRIDE; - int mode_(int m, const int *a) FL_OVERRIDE; - void swap_buffers() FL_OVERRIDE; - void resize(int is_a_resize, int w, int h) FL_OVERRIDE; - char swap_type() FL_OVERRIDE; - void swap_interval(int) FL_OVERRIDE; - int swap_interval() const FL_OVERRIDE; - Fl_Gl_Choice *find(int m, const int *alistp) FL_OVERRIDE; - GLContext create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) FL_OVERRIDE; - void set_gl_context(Fl_Window* w, GLContext context) FL_OVERRIDE; - void delete_gl_context(GLContext) FL_OVERRIDE; - void make_overlay_current() FL_OVERRIDE; - void redraw_overlay() FL_OVERRIDE; - void gl_start() FL_OVERRIDE; - void gl_visual(Fl_Gl_Choice *c) FL_OVERRIDE; - void init(); - void* GetProcAddress(const char *procName) FL_OVERRIDE; -public: - static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time); - //virtual bool need_scissor() { return true; } // CONTROL_LEAKING_SUB_GL_WINDOWS - //void apply_scissor(); // CONTROL_LEAKING_SUB_GL_WINDOWS -}; - -#endif // HAVE_GL -#endif // FL_WAYLAND_GL_WINDOW_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx deleted file mode 100644 index d20b941b7..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Gl_Window_Driver.cxx +++ /dev/null @@ -1,486 +0,0 @@ -// -// Class Fl_Wayland_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#if HAVE_GL -#include -#include -#include "../../Fl_Gl_Choice.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Gl_Window_Driver.H" -#include "../Posix/Fl_Posix_System_Driver.H" -#ifdef FLTK_USE_X11 -# include "../X11/Fl_X11_Gl_Window_Driver.H" -#endif -#include -#include -#include - -/* Implementation notes about OpenGL drawing on the Wayland platform - -* After eglCreateWindowSurface() with attributes {EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER, EGL_NONE}, -eglQueryContext() reports that EGL_RENDER_BUFFER equals EGL_BACK_BUFFER. -This experiment suggests that the platform only supports double-buffer drawing. -Consequently, FL_DOUBLE is enforced in all Fl_Gl_Window::mode_ values under Wayland. - -* Commented out code marked with CONTROL_LEAKING_SUB_GL_WINDOWS aims to prevent - sub GL windows from leaking out from their parent by making leaking parts fully transparent. - This code is commented out because it requires the FL_ALPHA flag to be on - which not all client applications do. -*/ - -// Describes crap needed to create a GLContext. -class Fl_Wayland_Gl_Choice : public Fl_Gl_Choice { - friend class Fl_Wayland_Gl_Window_Driver; -private: - EGLConfig egl_conf; -public: - Fl_Wayland_Gl_Choice(int m, const int *alistp, Fl_Gl_Choice *n) : Fl_Gl_Choice(m, alistp, n) { - egl_conf = 0; - } -}; - - -struct gl_start_support { // to support use of gl_start / gl_finish - struct wl_surface *surface; - struct wl_subsurface *subsurface; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; -}; - - -static EGLConfig wld_egl_conf = NULL; -static EGLint swap_interval_ = 1; -static EGLint max_swap_interval = 1000; -static EGLint min_swap_interval = 0; - - -EGLDisplay Fl_Wayland_Gl_Window_Driver::egl_display = EGL_NO_DISPLAY; - - -Fl_Wayland_Gl_Window_Driver::Fl_Wayland_Gl_Window_Driver(Fl_Gl_Window *win) : - Fl_Gl_Window_Driver(win) { - if (egl_display == EGL_NO_DISPLAY) init(); - egl_window = NULL; - egl_surface = NULL; - need_swap = false; -} - - -Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w) -{ -#ifdef FLTK_USE_X11 - if (!Fl_Wayland_Screen_Driver::wl_display) return new Fl_X11_Gl_Window_Driver(w); -#endif - return new Fl_Wayland_Gl_Window_Driver(w); -} - - -void Fl_Wayland_Gl_Window_Driver::init() { - EGLint major, minor; - - if (!fl_wl_display()) fl_open_display(); - egl_display = eglGetDisplay((EGLNativeDisplayType) fl_wl_display()); - if (egl_display == EGL_NO_DISPLAY) { - Fl::fatal("Can't create egl display\n"); - } - - if (eglInitialize(egl_display, &major, &minor) != EGL_TRUE) { - Fl::fatal("Can't initialise egl display\n"); - } - //printf("EGL major: %d, minor %d\n", major, minor); - //eglGetConfigs(egl_display, NULL, 0, &configs_count); - //printf("EGL has %d configs\n", configs_count); - eglBindAPI(EGL_OPENGL_API); -} - - -Fl_Gl_Choice *Fl_Wayland_Gl_Window_Driver::find(int m, const int *alistp) -{ - m |= FL_DOUBLE; - //if (pWindow->parent()) m |= FL_ALPHA; // CONTROL_LEAKING_SUB_GL_WINDOWS - Fl_Wayland_Gl_Choice *g = (Fl_Wayland_Gl_Choice*)Fl_Gl_Window_Driver::find_begin( - m, alistp); - if (g) return g; - - EGLint n; - EGLint config_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_DEPTH_SIZE, 0, // set at 11 - EGL_SAMPLE_BUFFERS, 0, // set at 13 - EGL_STENCIL_SIZE, 0, // set at 15 - EGL_ALPHA_SIZE, 0, // set at 17 - EGL_NONE - }; - - if (m & FL_DEPTH32) - config_attribs[11] = 32; // request at least 32 bits - else if (m & FL_DEPTH) - config_attribs[11] = 1; // accept any size - - if (m & FL_MULTISAMPLE) config_attribs[13] = 1; - if (m & FL_STENCIL) config_attribs[15] = 1; - if (m & FL_ALPHA) config_attribs[17] = (m & FL_RGB8) ? 8 : 1; - - g = new Fl_Wayland_Gl_Choice(m, alistp, first); - eglChooseConfig(egl_display, config_attribs, &(g->egl_conf), 1, &n); - if (n == 0 && (m & FL_MULTISAMPLE)) { - config_attribs[13] = 0; - eglChooseConfig(egl_display, config_attribs, &(g->egl_conf), 1, &n); - } - if (n == 0) { - Fl::fatal("failed to choose an EGL config\n"); - } - - eglGetConfigAttrib(egl_display, g->egl_conf, EGL_MAX_SWAP_INTERVAL, &max_swap_interval); - eglGetConfigAttrib(egl_display, g->egl_conf, EGL_MIN_SWAP_INTERVAL, &min_swap_interval); - - first = g; - return g; -} - - -GLContext Fl_Wayland_Gl_Window_Driver::create_gl_context(Fl_Window* window, - const Fl_Gl_Choice* g) { - GLContext shared_ctx = 0; - if (context_list && nContext) shared_ctx = context_list[0]; - - static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - GLContext ctx = (GLContext)eglCreateContext(egl_display, - ((Fl_Wayland_Gl_Choice*)g)->egl_conf, - (shared_ctx ? (EGLContext)shared_ctx : EGL_NO_CONTEXT), - context_attribs); -//fprintf(stderr, "eglCreateContext=%p shared_ctx=%p\n", ctx, shared_ctx); - if (ctx) { - add_context(ctx); - /* CONTROL_LEAKING_SUB_GL_WINDOWS - if (egl_surface) { - eglMakeCurrent(egl_display, egl_surface, egl_surface, (EGLContext)ctx); - glClearColor(0., 0., 0., 1.); // set opaque black as starting background color - apply_scissor(); - }*/ - } - return ctx; -} - - -void Fl_Wayland_Gl_Window_Driver::set_gl_context(Fl_Window* w, GLContext context) { - struct wld_window *win = fl_wl_xid(w); - if (!win) return; - Fl_Wayland_Window_Driver *dr = Fl_Wayland_Window_Driver::driver(w); - EGLSurface target_egl_surface = NULL; - if (egl_surface) target_egl_surface = egl_surface; - else if (dr->gl_start_support_) target_egl_surface = dr->gl_start_support_->egl_surface; - if (!target_egl_surface) { // useful for gl_start() - dr->gl_start_support_ = new struct gl_start_support; - float s = Fl::screen_scale(w->screen_num()); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - // the GL scene will be a transparent subsurface above the cairo-drawn surface - dr->gl_start_support_->surface = - wl_compositor_create_surface(scr_driver->wl_compositor); - dr->gl_start_support_->subsurface = wl_subcompositor_get_subsurface( - scr_driver->wl_subcompositor, dr->gl_start_support_->surface, win->wl_surface); - wl_subsurface_set_position(dr->gl_start_support_->subsurface, w->x() * s, w->y() * s); - wl_subsurface_place_above(dr->gl_start_support_->subsurface, win->wl_surface); - dr->gl_start_support_->egl_window = wl_egl_window_create( - dr->gl_start_support_->surface, w->w() * s, w->h() * s); - target_egl_surface = dr->gl_start_support_->egl_surface = eglCreateWindowSurface( - egl_display, wld_egl_conf, dr->gl_start_support_->egl_window, NULL); - } - GLContext current_context = eglGetCurrentContext(); - if (context != current_context || w != cached_window) { - cached_window = w; - if (eglMakeCurrent(egl_display, target_egl_surface, target_egl_surface, - (EGLContext)context)) { -//fprintf(stderr, "EGLContext %p made current\n", context); - } else { - Fl::error("eglMakeCurrent() failed\n"); - } - } - if (!(mode() & FL_ALPHA)) { // useful at least for Linux on MacBook hardware - GLfloat vals[4]; - glGetFloatv(GL_COLOR_CLEAR_VALUE, vals); - if (vals[3] == 0.) glClearColor(vals[0], vals[1], vals[2], 1.); - } -} - -/* CONTROL_LEAKING_SUB_GL_WINDOWS -void Fl_Wayland_Gl_Window_Driver::apply_scissor() { - cairo_rectangle_int_t *extents = Fl_Wayland_Window_Driver::driver(pWindow)->subRect(); - if (extents) { - glDisable(GL_SCISSOR_TEST); - GLdouble vals[4]; - glGetDoublev(GL_COLOR_CLEAR_VALUE, vals); - glClearColor(0., 0., 0., 0.); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(vals[0], vals[1], vals[2], vals[3]); - float s = pWindow->pixels_per_unit(); - glScissor(s*extents->x, s*extents->y, s*extents->width, s*extents->height); -//printf("apply_scissor %dx%d %dx%d\n",extents->x, extents->y, extents->width, extents->height); - glEnable(GL_SCISSOR_TEST); - } -}*/ - - -void Fl_Wayland_Gl_Window_Driver::delete_gl_context(GLContext context) { - GLContext current_context = eglGetCurrentContext(); - if (current_context == context) { - cached_window = 0; - } - if (current_context == (EGLContext)context) { - eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - eglDestroyContext(egl_display, (EGLContext)context); - eglDestroySurface(egl_display, egl_surface); - egl_surface = NULL; - wl_egl_window_destroy(egl_window); - egl_window = NULL; - del_context(context); -} - - -void Fl_Wayland_Gl_Window_Driver::make_overlay_current() { - glDrawBuffer(GL_FRONT); -} - - -void Fl_Wayland_Gl_Window_Driver::redraw_overlay() { - pWindow->redraw(); -} - - -void Fl_Wayland_Gl_Window_Driver::make_current_before() { - if (!egl_window) { - struct wld_window *win = fl_wl_xid(pWindow); - struct wl_surface *surface = win->wl_surface; - int W = pWindow->pixel_w(); - int H = pWindow->pixel_h(); - int scale = Fl_Wayland_Window_Driver::driver(pWindow)->wld_scale(); - egl_window = wl_egl_window_create(surface, (W/scale)*scale, (H/scale)*scale); - if (egl_window == EGL_NO_SURFACE) { - Fl::fatal("Can't create egl window with wl_egl_window_create()\n"); - } - Fl_Wayland_Gl_Choice *g = (Fl_Wayland_Gl_Choice*)this->g(); - egl_surface = eglCreateWindowSurface(egl_display, g->egl_conf, egl_window, NULL); - wl_surface_set_buffer_scale(surface, scale); - if (mode() & FL_ALPHA) wl_surface_set_opaque_region(surface, NULL); - // Tested apps: shape, glpuzzle, cube, fractals, gl_overlay, fullscreen, unittests, - // OpenGL3-glut-test, OpenGL3test. - // Tested wayland compositors: mutter, kde-plasma, weston, sway on FreeBSD. - if (pWindow->parent()) win = fl_wl_xid(pWindow->top_window()); - while (wl_list_empty(&win->outputs)) wl_display_dispatch(fl_wl_display()); - } -} - - -float Fl_Wayland_Gl_Window_Driver::pixels_per_unit() -{ - int ns = pWindow->screen_num(); - int wld_scale = (pWindow->shown() ? - Fl_Wayland_Window_Driver::driver(pWindow)->wld_scale() : 1); - return wld_scale * Fl::screen_driver()->scale(ns); -} - - -int Fl_Wayland_Gl_Window_Driver::mode_(int m, const int *a) { - mode(m | FL_DOUBLE); - return 1; -} - - -void Fl_Wayland_Gl_Window_Driver::surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - Fl_Wayland_Gl_Window_Driver *gl_dr = (Fl_Wayland_Gl_Window_Driver *)data; - wl_callback_destroy(cb); - struct wld_window *window = fl_wl_xid(gl_dr->pWindow); - window->frame_cb = NULL; - if (gl_dr->need_swap) { - eglSwapBuffers(Fl_Wayland_Gl_Window_Driver::egl_display, gl_dr->egl_surface); - gl_dr->need_swap = false; - } -} - - -static const struct wl_callback_listener surface_frame_listener = { - .done = Fl_Wayland_Gl_Window_Driver::surface_frame_done, -}; - - -void Fl_Wayland_Gl_Window_Driver::swap_buffers() { - if (overlay()) { - static bool overlay_buffer = true; - int wo = pWindow->pixel_w(), ho = pWindow->pixel_h(); - GLint matrixmode; - GLfloat pos[4]; - glGetIntegerv(GL_MATRIX_MODE, &matrixmode); - glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); // save original glRasterPos - glMatrixMode(GL_PROJECTION); // save proj/model matrices - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glScalef(2.0f/wo, 2.0f/ho, 1.0f); - glTranslatef(-wo/2.0f, -ho/2.0f, 0.0f); // set transform so 0,0 is bottom/left of window - glRasterPos2i(0,0); // set glRasterPos to bottom left corner - { - // Emulate overlay by doing copypixels - glReadBuffer(overlay_buffer?GL_BACK:GL_FRONT); - glDrawBuffer(overlay_buffer?GL_FRONT:GL_BACK); - overlay_buffer = ! overlay_buffer; - glCopyPixels(0, 0, wo, ho, GL_COLOR); - } - glPopMatrix(); // GL_MODELVIEW // restore model/proj matrices - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(matrixmode); - glRasterPos3f(pos[0], pos[1], pos[2]); // restore original glRasterPos - if (!overlay_buffer) return; // don't call eglSwapBuffers until overlay has been drawn - } - - if (egl_surface) { - Fl_Window *parent = pWindow->parent() ? pWindow->window() : NULL; - struct wld_window *parent_xid = parent ? fl_wl_xid(parent) : NULL; - if (parent_xid) { // issue #967 - struct wld_window *xid = fl_wl_xid(pWindow); - if (xid->frame_cb) { - need_swap = true; - return; - } - if (!parent_xid->frame_cb) { - xid->frame_cb = wl_surface_frame(xid->wl_surface); - wl_callback_add_listener(xid->frame_cb, &surface_frame_listener, this); - } - } - eglSwapBuffers(Fl_Wayland_Gl_Window_Driver::egl_display, egl_surface); - need_swap = false; - } -} - - -class Fl_Wayland_Gl_Plugin : public Fl_Wayland_Plugin { -public: - Fl_Wayland_Gl_Plugin() : Fl_Wayland_Plugin(name()) { } - const char *name() FL_OVERRIDE { return "gl.wayland.fltk.org"; } - void do_swap(Fl_Window *w) FL_OVERRIDE { - Fl_Gl_Window_Driver *gldr = Fl_Gl_Window_Driver::driver(w->as_gl_window()); - if (gldr->overlay() == w) gldr->swap_buffers(); - } - void invalidate(Fl_Window *w) FL_OVERRIDE { - w->as_gl_window()->valid(0); - } - void terminate() FL_OVERRIDE { - if (Fl_Wayland_Gl_Window_Driver::egl_display != EGL_NO_DISPLAY) { - eglTerminate(Fl_Wayland_Gl_Window_Driver::egl_display); - } - } - void destroy(struct gl_start_support *gl_start_support_) FL_OVERRIDE { - eglDestroySurface(Fl_Wayland_Gl_Window_Driver::egl_display, - gl_start_support_->egl_surface); - wl_egl_window_destroy(gl_start_support_->egl_window); - wl_subsurface_destroy(gl_start_support_->subsurface); - wl_surface_destroy(gl_start_support_->surface); - delete gl_start_support_; - } -}; - - -static Fl_Wayland_Gl_Plugin Gl_Overlay_Plugin; - - -/* CONTROL_LEAKING_SUB_GL_WINDOWS -static void delayed_scissor(Fl_Wayland_Gl_Window_Driver *dr) { - dr->apply_scissor(); -}*/ - - -void Fl_Wayland_Gl_Window_Driver::resize(int is_a_resize, int W, int H) { - if (!egl_window) return; - float f = Fl::screen_scale(pWindow->screen_num()); - int s = Fl_Wayland_Window_Driver::driver(pWindow)->wld_scale(); - W = int(W * f) * s; // W, H must be multiples of int s - H = int(H * f) * s; - int W2, H2; - wl_egl_window_get_attached_size(egl_window, &W2, &H2); - if (W2 != W || H2 != H) { - struct wld_window *xid = fl_wl_xid(pWindow); - if (xid->kind == Fl_Wayland_Window_Driver::DECORATED && !xid->frame_cb) { - xid->frame_cb = wl_surface_frame(xid->wl_surface); - wl_callback_add_listener(xid->frame_cb, - Fl_Wayland_Graphics_Driver::p_surface_frame_listener, xid); - } - wl_egl_window_resize(egl_window, W, H, 0, 0); - wl_surface_set_buffer_scale(xid->wl_surface, s); - } - /* CONTROL_LEAKING_SUB_GL_WINDOWS - if (Fl_Wayland_Window_Driver::driver(pWindow)->subRect()) { - pWindow->redraw(); - Fl::add_timeout(0.01, (Fl_Timeout_Handler)delayed_scissor, this); - }*/ -} - - -char Fl_Wayland_Gl_Window_Driver::swap_type() { - return copy; -} - - -void Fl_Wayland_Gl_Window_Driver::gl_visual(Fl_Gl_Choice *c) { - Fl_Gl_Window_Driver::gl_visual(c); - wld_egl_conf = ((Fl_Wayland_Gl_Choice*)c)->egl_conf; -} - - -void Fl_Wayland_Gl_Window_Driver::gl_start() { - float f = Fl::screen_scale(Fl_Window::current()->screen_num()); - int W = Fl_Window::current()->w() * f; - int H = Fl_Window::current()->h() * f; - int W2, H2; - Fl_Wayland_Window_Driver *dr = Fl_Wayland_Window_Driver::driver(Fl_Window::current()); - wl_egl_window_get_attached_size(dr->gl_start_support_->egl_window, &W2, &H2); - if (W2 != W || H2 != H) { - wl_egl_window_resize(dr->gl_start_support_->egl_window, W, H, 0, 0); - } - glClearColor(0., 0., 0., 0.); - glClear(GL_COLOR_BUFFER_BIT); -} - -void Fl_Wayland_Gl_Window_Driver::swap_interval(int interval) { - if (interval < min_swap_interval) interval = min_swap_interval; - if (interval > max_swap_interval) interval = max_swap_interval; - if (egl_display && eglSwapInterval(egl_display, interval)) - swap_interval_ = interval; - // printf("swap_interval_=%d\n",swap_interval_); -} - - -int Fl_Wayland_Gl_Window_Driver::swap_interval() const { - return swap_interval_; -} - - -void* Fl_Wayland_Gl_Window_Driver::GetProcAddress(const char *procName) { - return Fl_Posix_System_Driver::dlopen_or_dlsym(NULL, procName); -} - - -FL_EXPORT EGLContext fl_wl_glcontext(GLContext rc) { return (EGLContext)rc; } - -#endif // HAVE_GL diff --git a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H deleted file mode 100644 index ac8786a47..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.H +++ /dev/null @@ -1,72 +0,0 @@ -// -// Definition of class Fl_Wayland_Graphics_Driver. -// -// Copyright 2021-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Wayland_Graphics_Driver.H - \brief Definition of Wayland graphics driver. - */ - -#ifndef FL_WAYLAND_GRAPHICS_DRIVER_H -#define FL_WAYLAND_GRAPHICS_DRIVER_H - -#include "../Cairo/Fl_Cairo_Graphics_Driver.H" -#include // for uint32_t -#include // for wl_list - - -class Fl_Wayland_Graphics_Driver : public Fl_Cairo_Graphics_Driver { -public: - struct draw_buffer { - unsigned char *buffer; - cairo_t *cairo_; - size_t data_size; // of wl_buffer and buffer - int stride; - int width; - }; - struct wld_buffer { - struct draw_buffer draw_buffer; - struct wl_list link; // links all buffers from the same wl_shm_pool - struct wl_buffer *wl_buffer; - void *data; - struct wl_shm_pool *shm_pool; - bool draw_buffer_needs_commit; - bool in_use; // true while being committed - bool released; // true after buffer_release() was called - }; - struct wld_shm_pool_data { // one record attached to each wl_shm_pool object - char *pool_memory; // start of mmap'ed memory encapsulated by the wl_shm_pool - size_t pool_size; // size of encapsulated memory - struct wl_list buffers; // to list of fl_wld_buffer's from this pool - }; - static const uint32_t wld_format; - static struct wl_shm_pool *current_pool; - static FL_EXPORT const struct wl_callback_listener *p_surface_frame_listener; - void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen osrc, - int srcx, int srcy) FL_OVERRIDE; - void cache_size(Fl_Image *img, int &width, int &height) FL_OVERRIDE; - static struct wld_buffer *create_wld_buffer(int width, int height, bool with_shm = true); - static void create_shm_buffer(wld_buffer *buffer); - static void buffer_release(struct wld_window *window); - static void buffer_commit(struct wld_window *window, cairo_region_t *r = NULL); - static void cairo_init(struct draw_buffer *buffer, int width, int height, int stride, - cairo_format_t format); - // used by class Fl_Wayland_Gl_Window_Driver - static FL_EXPORT struct draw_buffer *offscreen_buffer(Fl_Offscreen); - static const cairo_user_data_key_t key; - static Fl_Image_Surface *custom_offscreen(int w, int h, struct wld_buffer **buffer); -}; - -#endif // FL_WAYLAND_GRAPHICS_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx deleted file mode 100644 index 5c9539a8c..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Graphics_Driver.cxx +++ /dev/null @@ -1,310 +0,0 @@ -// -// Implementation of the Wayland graphics driver. -// -// Copyright 2021-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include -#include -#include // for close() -#include -#include // for strerror() -#include - -extern "C" { -# include "../../../libdecor/src/os-compatibility.h" // for libdecor_os_create_anonymous_file() -} - -// used by create_shm_buffer and do_buffer_release -struct wl_shm_pool *Fl_Wayland_Graphics_Driver::current_pool = NULL; - - -static void do_buffer_release(struct Fl_Wayland_Graphics_Driver::wld_buffer *); - - -static void buffer_release_listener(void *user_data, struct wl_buffer *wl_buffer) -{ - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = - (struct Fl_Wayland_Graphics_Driver::wld_buffer*)user_data; - buffer->in_use = false; - if (buffer->released) do_buffer_release(buffer); -} - - -static const struct wl_buffer_listener buffer_listener = { - buffer_release_listener -}; - - -void Fl_Wayland_Graphics_Driver::create_shm_buffer(Fl_Wayland_Graphics_Driver::wld_buffer *buffer) { - int width = buffer->draw_buffer.width; - int stride = buffer->draw_buffer.stride; - int height = buffer->draw_buffer.data_size / stride; - const size_t default_pool_size = 10000000; // larger pools are possible if needed - int chunk_offset = 0; // offset to start of available memory in pool - struct wld_shm_pool_data *pool_data = current_pool ? // data record attached to current pool - (struct wld_shm_pool_data *)wl_shm_pool_get_user_data(current_pool) : NULL; - size_t pool_size = current_pool ? pool_data->pool_size : default_pool_size; // current pool size - if (current_pool && !wl_list_empty(&pool_data->buffers)) { - // last wld_buffer created from current pool - struct wld_buffer *record = wl_container_of(pool_data->buffers.next, record, link); - chunk_offset = ((char*)record->data - pool_data->pool_memory) + - record->draw_buffer.data_size; - } - if (!current_pool || chunk_offset + buffer->draw_buffer.data_size > pool_size) { - // if true, a new pool is needed - if (current_pool && wl_list_empty(&pool_data->buffers)) { - wl_shm_pool_destroy(current_pool); - /*int err = */munmap(pool_data->pool_memory, pool_data->pool_size); -// printf("create_shm_buffer munmap(%p)->%d\n", pool_data->pool_memory, err); - free(pool_data); - } - chunk_offset = 0; - pool_size = default_pool_size; - if (buffer->draw_buffer.data_size > pool_size) - pool_size = 2 * buffer->draw_buffer.data_size; // a larger pool is needed - int fd = libdecor_os_create_anonymous_file(pool_size); - if (fd < 0) { - Fl::fatal("libdecor_os_create_anonymous_file failed: %s\n", strerror(errno)); - } - pool_data = (struct wld_shm_pool_data*)calloc(1, sizeof(struct wld_shm_pool_data)); - pool_data->pool_memory = (char*)mmap(NULL, pool_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if (pool_data->pool_memory == MAP_FAILED) { - close(fd); - Fl::fatal("mmap failed: %s\n", strerror(errno)); - } - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - current_pool = wl_shm_create_pool(scr_driver->wl_shm, fd, (int32_t)pool_size); - close(fd); // does not prevent the mmap'ed memory from being used - //printf("wl_shm_create_pool %p size=%lu\n",pool_data->pool_memory , pool_size); - pool_data->pool_size = pool_size; - wl_list_init(&pool_data->buffers); - wl_shm_pool_set_user_data(current_pool, pool_data); - } - buffer->wl_buffer = wl_shm_pool_create_buffer(current_pool, chunk_offset, - width, height, stride, wld_format); - wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer); - // add this buffer to head of list of current pool's buffers - wl_list_insert(&pool_data->buffers, &buffer->link); - buffer->shm_pool = current_pool; - buffer->data = (void*)(pool_data->pool_memory + chunk_offset); -//fprintf(stderr, "last=%p chunk_offset=%d ", pool_data->buffers.next, chunk_offset); -//fprintf(stderr, "create_shm_buffer: %dx%d = %d\n", width, height, size); -} - - -struct Fl_Wayland_Graphics_Driver::wld_buffer * - Fl_Wayland_Graphics_Driver::create_wld_buffer(int width, int height, bool with_shm) { - struct wld_buffer *buffer = (struct wld_buffer*)calloc(1, sizeof(struct wld_buffer)); - int stride = cairo_format_stride_for_width(cairo_format, width); - cairo_init(&buffer->draw_buffer, width, height, stride, cairo_format); - buffer->draw_buffer_needs_commit = true; - if (with_shm) create_shm_buffer(buffer); - return buffer; -} - - -// used to support both normal and progressive drawing and for top-level GL windows -static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - struct wld_window *window = (struct wld_window *)data; - wl_callback_destroy(cb); - window->frame_cb = NULL; - if (window->buffer && window->buffer->draw_buffer_needs_commit) { - Fl_Wayland_Graphics_Driver::buffer_commit(window); - } -} - - -static const struct wl_callback_listener surface_frame_listener = { - .done = surface_frame_done, -}; - - -const struct wl_callback_listener *Fl_Wayland_Graphics_Driver::p_surface_frame_listener = - &surface_frame_listener; - - -// copy pixels in region r from the Cairo surface to the Wayland buffer -static void copy_region(struct wld_window *window, cairo_region_t *r) { - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = window->buffer; - float f = Fl::screen_scale(window->fl_win->screen_num()); - int d = Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale(); - int count = cairo_region_num_rectangles(r); - cairo_rectangle_int_t rect; - for (int i = 0; i < count; i++) { - cairo_region_get_rectangle(r, i, &rect); - int left = d * int(rect.x * f); - int top = d * int(rect.y * f); - int right = d * ceil((rect.x + rect.width) * f); - if (right > d * int(window->fl_win->w() * f)) right = d * int(window->fl_win->w() * f); - int width = right - left; - int bottom = d * ceil((rect.y + rect.height) * f); - if (bottom > d * int(window->fl_win->h() * f)) bottom = d * int(window->fl_win->h() * f); - int height = bottom - top; - int offset = top * buffer->draw_buffer.stride + 4 * left; - int W4 = 4 * width; - for (int l = 0; l < height; l++) { - if (offset + W4 >= (int)buffer->draw_buffer.data_size) { - W4 = buffer->draw_buffer.data_size - offset; - if (W4 <= 0) break; - } - memcpy((uchar*)buffer->data + offset, buffer->draw_buffer.buffer + offset, W4); - offset += buffer->draw_buffer.stride; - } - wl_surface_damage_buffer(window->wl_surface, left, top, width, height); - } -} - - -void Fl_Wayland_Graphics_Driver::buffer_commit(struct wld_window *window, cairo_region_t *r) -{ - if (!window->buffer->wl_buffer) create_shm_buffer(window->buffer); - cairo_surface_t *surf = cairo_get_target(window->buffer->draw_buffer.cairo_); - cairo_surface_flush(surf); - if (r) copy_region(window, r); - else { - memcpy(window->buffer->data, window->buffer->draw_buffer.buffer, - window->buffer->draw_buffer.data_size); - wl_surface_damage_buffer(window->wl_surface, 0, 0, 1000000, 1000000); - } - window->buffer->in_use = true; - wl_surface_attach(window->wl_surface, window->buffer->wl_buffer, 0, 0); - wl_surface_set_buffer_scale( window->wl_surface, - Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale() ); - if (!window->covered) { // see issue #878 - window->frame_cb = wl_surface_frame(window->wl_surface); - wl_callback_add_listener(window->frame_cb, p_surface_frame_listener, window); - } - wl_surface_commit(window->wl_surface); - window->buffer->draw_buffer_needs_commit = false; -} - - -void Fl_Wayland_Graphics_Driver::cairo_init(struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer, - int width, int height, int stride, - cairo_format_t format) { - buffer->data_size = stride * height; - buffer->stride = stride; - buffer->buffer = new uchar[buffer->data_size]; - buffer->width = width; - cairo_surface_t *surf = cairo_image_surface_create_for_data(buffer->buffer, format, - width, height, stride); - if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { - Fl::fatal("Can't create Cairo surface with cairo_image_surface_create_for_data()\n"); - return; - } - buffer->cairo_ = cairo_create(surf); - cairo_status_t err; - if ((err = cairo_status(buffer->cairo_)) != CAIRO_STATUS_SUCCESS) { - Fl::fatal("Cairo error during cairo_create() %s\n", cairo_status_to_string(err)); - return; - } - cairo_surface_destroy(surf); - memset(buffer->buffer, 0, buffer->data_size); // useful for transparent windows - cairo_set_source_rgba(buffer->cairo_, .0, .0, .0, 1.0); // Black default color - cairo_save(buffer->cairo_); -} - - -// runs when buffer->in_use is false and buffer->released is true -static void do_buffer_release(struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer) { - struct wl_shm_pool *my_pool = buffer->shm_pool; - if (buffer->wl_buffer) { - struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data *pool_data = - (struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data*) - wl_shm_pool_get_user_data(my_pool); - wl_buffer_destroy(buffer->wl_buffer); - // remove wld_buffer from list of pool's buffers - wl_list_remove(&buffer->link); - if (wl_list_empty(&pool_data->buffers) && my_pool != Fl_Wayland_Graphics_Driver::current_pool) { - // all buffers from pool are gone - wl_shm_pool_destroy(my_pool); - /*int err = */munmap(pool_data->pool_memory, pool_data->pool_size); - //printf("do_buffer_release munmap(%p)->%d\n", pool_data->pool_memory, err); - free(pool_data); - } - } - free(buffer); -} - - -void Fl_Wayland_Graphics_Driver::buffer_release(struct wld_window *window) -{ - if (window->buffer && !window->buffer->released) { - window->buffer->released = true; - if (window->frame_cb) { wl_callback_destroy(window->frame_cb); window->frame_cb = NULL; } - delete[] window->buffer->draw_buffer.buffer; - window->buffer->draw_buffer.buffer = NULL; - cairo_destroy(window->buffer->draw_buffer.cairo_); - if (!window->buffer->in_use) do_buffer_release(window->buffer); - window->buffer = NULL; - } -} - - -// this refers to the same memory layout for pixel data as does CAIRO_FORMAT_ARGB32 -const uint32_t Fl_Wayland_Graphics_Driver::wld_format = WL_SHM_FORMAT_ARGB8888; - - -void Fl_Wayland_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, - Fl_Offscreen src, int srcx, int srcy) { - // draw portion srcx,srcy,w,h of osrc to position x,y (top-left) of - // the graphics driver's surface - cairo_matrix_t matrix; - cairo_get_matrix(cairo_, &matrix); - double s = matrix.xx; - cairo_save(cairo_); - cairo_rectangle(cairo_, x - 0.5, y - 0.5, w, h); - cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_NONE); - cairo_clip(cairo_); - cairo_set_antialias(cairo_, CAIRO_ANTIALIAS_DEFAULT); - cairo_surface_t *surf = cairo_get_target((cairo_t *)src); - cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf); - cairo_set_source(cairo_, pat); - cairo_matrix_init_scale(&matrix, s, s); - cairo_matrix_translate(&matrix, -(x - srcx), -(y - srcy)); - cairo_pattern_set_matrix(pat, &matrix); - cairo_paint(cairo_); - cairo_pattern_destroy(pat); - cairo_restore(cairo_); - surface_needs_commit(); -} - - -const cairo_user_data_key_t Fl_Wayland_Graphics_Driver::key = {}; - - -struct Fl_Wayland_Graphics_Driver::draw_buffer* -Fl_Wayland_Graphics_Driver::offscreen_buffer(Fl_Offscreen offscreen) { - return (struct draw_buffer*)cairo_get_user_data((cairo_t*)offscreen, &key); -} - - -Fl_Image_Surface *Fl_Wayland_Graphics_Driver::custom_offscreen(int w, int h, - struct Fl_Wayland_Graphics_Driver::wld_buffer **p_off) { - struct wld_buffer *off = create_wld_buffer(w, h); - *p_off = off; - cairo_set_user_data(off->draw_buffer.cairo_, &key, &off->draw_buffer, NULL); - return new Fl_Image_Surface(w, h, 0, (Fl_Offscreen)off->draw_buffer.cairo_); -} - - -void Fl_Wayland_Graphics_Driver::cache_size(Fl_Image *img, int &width, int &height) { - Fl_Graphics_Driver::cache_size(img, width, height); - width *= wld_scale; - height *= wld_scale; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H deleted file mode 100644 index ae32ac3df..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.H +++ /dev/null @@ -1,40 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef FL_WAYLAND_IMAGE_SURFACE_DRIVER_H -#define FL_WAYLAND_IMAGE_SURFACE_DRIVER_H - -#include - -class Fl_Wayland_Image_Surface_Driver : public Fl_Image_Surface_Driver { - void end_current() FL_OVERRIDE; - struct wld_window *pre_window; -public: - Fl_Wayland_Image_Surface_Driver(int w, int h, int high_res, Fl_Offscreen off); - ~Fl_Wayland_Image_Surface_Driver(); - void mask(const Fl_RGB_Image *) FL_OVERRIDE; - struct shape_data_type { - double scale; - cairo_pattern_t *mask_pattern_; - cairo_t *bg_cr; - } *shape_data_; - void set_current() FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate() FL_OVERRIDE; - Fl_RGB_Image *image() FL_OVERRIDE; -}; - -#endif // FL_WAYLAND_IMAGE_SURFACE_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx deleted file mode 100644 index ec9c56cb7..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Image_Surface_Driver.cxx +++ /dev/null @@ -1,185 +0,0 @@ -// -// Draw-to-image code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Image_Surface_Driver.H" - - -Fl_Wayland_Image_Surface_Driver::Fl_Wayland_Image_Surface_Driver(int w, int h, - int high_res, Fl_Offscreen off) : Fl_Image_Surface_Driver(w, h, high_res, off) { - shape_data_ = NULL; - float s = 1; - int d = 1; - if (!off) { - fl_open_display(); - if (Fl::first_window()) { - d = Fl_Wayland_Window_Driver::driver(Fl::first_window())->wld_scale(); - } - s = Fl_Graphics_Driver::default_driver().scale(); - if (d*s != 1 && high_res) { - w = int(w * s) * d; - h = int(h * s) * d; - } - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - (struct Fl_Wayland_Graphics_Driver::draw_buffer*)calloc(1, - sizeof(struct Fl_Wayland_Graphics_Driver::draw_buffer)); - Fl_Wayland_Graphics_Driver::cairo_init(off_, w, h, - cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w), CAIRO_FORMAT_RGB24); - offscreen = (Fl_Offscreen)off_->cairo_; - cairo_set_user_data(off_->cairo_, &Fl_Wayland_Graphics_Driver::key, off_, NULL); - if (d*s != 1 && high_res) cairo_scale((cairo_t*)offscreen, d*s, d*s); - } - driver(new Fl_Wayland_Graphics_Driver()); - if (d*s != 1 && high_res) driver()->scale(d*s); -} - - -Fl_Wayland_Image_Surface_Driver::~Fl_Wayland_Image_Surface_Driver() { - if (shape_data_) { - cairo_surface_t *surf; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf); - unsigned char *bits = cairo_image_surface_get_data(surf); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] bits; - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - Fl_Wayland_Graphics_Driver::offscreen_buffer((Fl_Offscreen)shape_data_->bg_cr); - delete[] off_->buffer; - free(off_); - cairo_destroy(shape_data_->bg_cr); - free(shape_data_); - } - if (offscreen && !external_offscreen) { - struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer = - Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); - cairo_destroy((cairo_t *)offscreen); - delete[] buffer->buffer; - free(buffer); - } - delete driver(); -} - - -void Fl_Wayland_Image_Surface_Driver::set_current() { - Fl_Surface_Device::set_current(); - Fl_Cairo_Graphics_Driver *dr = (Fl_Cairo_Graphics_Driver*)driver(); - if (!dr->cr()) dr->set_cairo((cairo_t*)offscreen); - pre_window = Fl_Wayland_Window_Driver::wld_window; - Fl_Wayland_Window_Driver::wld_window = NULL; - fl_window = 0; -} - - -void Fl_Wayland_Image_Surface_Driver::end_current() { - cairo_surface_t *surf = cairo_get_target((cairo_t*)offscreen); - cairo_surface_flush(surf); - Fl_Wayland_Window_Driver::wld_window = pre_window; - fl_window = (Window)pre_window; - Fl_Surface_Device::end_current(); -} - - -void Fl_Wayland_Image_Surface_Driver::translate(int x, int y) { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_translate(x, y); -} - - -void Fl_Wayland_Image_Surface_Driver::untranslate() { - ((Fl_Wayland_Graphics_Driver*)driver())->ps_untranslate(); -} - - -Fl_RGB_Image* Fl_Wayland_Image_Surface_Driver::image() { - if (shape_data_ && shape_data_->mask_pattern_) { - // draw above the secondary offscreen the main offscreen masked by mask_pattern_ - cairo_t *c = ((Fl_Cairo_Graphics_Driver*)driver())->cr(); - cairo_pattern_t *paint_pattern = cairo_pattern_create_for_surface(cairo_get_target(c)); - cairo_set_source(shape_data_->bg_cr, paint_pattern); - cairo_mask(shape_data_->bg_cr, shape_data_->mask_pattern_); - cairo_pattern_destroy(paint_pattern); - // copy secondary offscreen to the main offscreen - cairo_pattern_t *pat = cairo_pattern_create_for_surface(cairo_get_target(shape_data_->bg_cr)); - cairo_scale(c, shape_data_->scale, shape_data_->scale); - cairo_set_source(c, pat), - cairo_paint(c); - cairo_pattern_destroy(pat); - // delete secondary offscreen - cairo_surface_t *surf; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surf); - unsigned char *bits = cairo_image_surface_get_data(surf); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] bits; - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - Fl_Wayland_Graphics_Driver::offscreen_buffer((Fl_Offscreen)shape_data_->bg_cr); - delete[] off_->buffer; - free(off_); - cairo_destroy(shape_data_->bg_cr); - free(shape_data_); - shape_data_ = NULL; - } - - // Convert depth-4 image in draw_buffer to a depth-3 image while exchanging R and B colors - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf = - Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); - int height = int(off_buf->data_size / off_buf->stride); - uchar *rgb = new uchar[off_buf->width * height * 3]; - uchar *p = rgb; - uchar *q; - for (int j = 0; j < height; j++) { - q = off_buf->buffer + j*off_buf->stride; - for (int i = 0; i < off_buf->width; i++) { // exchange R and B colors, transmit G - *p = *(q+2); - *(p+1) = *(q+1); - *(p+2) = *q; - p += 3; q += 4; - } - } - Fl_RGB_Image *image = new Fl_RGB_Image(rgb, off_buf->width, height, 3); - image->alloc_array = 1; - return image; -} - - -void Fl_Wayland_Image_Surface_Driver::mask(const Fl_RGB_Image *mask) { - bool using_copy = false; - shape_data_ = (struct shape_data_type*)calloc(1, sizeof(struct shape_data_type)); - int W, H; - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_buf = - Fl_Wayland_Graphics_Driver::offscreen_buffer(offscreen); - W = off_buf->width; - H = (int)(off_buf->data_size / off_buf->stride); - if (W != mask->data_w() || H != mask->data_h()) { - Fl_RGB_Image *copy = (Fl_RGB_Image*)mask->copy(W, H); - mask = copy; - using_copy = true; - } - shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::calc_cairo_mask(mask); - //duplicate current offscreen content to new cairo_t* shape_data_->bg_cr - int width, height; - printable_rect(&width, &height); - struct Fl_Wayland_Graphics_Driver::draw_buffer *off_ = - (struct Fl_Wayland_Graphics_Driver::draw_buffer*)calloc(1, - sizeof(struct Fl_Wayland_Graphics_Driver::draw_buffer)); - Fl_Wayland_Graphics_Driver::cairo_init(off_, W, H, - cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, W), - CAIRO_FORMAT_RGB24); - cairo_set_user_data(off_->cairo_, &Fl_Wayland_Graphics_Driver::key, off_, NULL); - shape_data_->bg_cr = off_->cairo_; - memcpy(off_->buffer, off_buf->buffer, off_buf->data_size); - shape_data_->scale = double(width) / W; - if (using_copy) delete mask; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H deleted file mode 100644 index 83efd79a3..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.H +++ /dev/null @@ -1,193 +0,0 @@ -// -// Definition of the Wayland Screen interface -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Wayland_Screen_Driver.H - \brief Definition of Wayland Screen interface - */ - -#ifndef FL_WAYLAND_SCREEN_DRIVER_H -#define FL_WAYLAND_SCREEN_DRIVER_H - -#include -#include "../Unix/Fl_Unix_Screen_Driver.H" -#include - -class Fl_Window; - -class Fl_Wayland_Screen_Driver : public Fl_Unix_Screen_Driver -{ -private: - static int insertion_point_x; - static int insertion_point_y; - static int insertion_point_width; - static int insertion_point_height; - static bool insertion_point_location_is_valid; -public: -// type definitions - typedef enum {unspecified, MUTTER, WESTON, KWIN, OWL, WAYFIRE} compositor_name; - struct seat { - struct wl_seat *wl_seat; - struct wl_pointer *wl_pointer; - struct wl_keyboard *wl_keyboard; - uint32_t keyboard_enter_serial; - struct wl_surface *keyboard_surface; - struct wl_list pointer_outputs; - struct wl_cursor_theme *cursor_theme; - struct wl_cursor *default_cursor; - struct wl_surface *cursor_surface; - struct wl_surface *pointer_focus; - int pointer_scale; - uint32_t serial; - uint32_t pointer_enter_serial; - struct wl_data_device_manager *data_device_manager; - struct wl_data_device *data_device; - struct wl_data_source *data_source; - struct xkb_state *xkb_state; - struct xkb_context *xkb_context; - struct xkb_keymap *xkb_keymap; - struct xkb_compose_state *xkb_compose_state; - char *name; - struct zwp_text_input_v3 *text_input; - struct gtk_shell1 *gtk_shell; - }; - struct output { // one record for each screen - uint32_t id; - int x, y; // logical position of screen - int pixel_width; // in pixels - int pixel_height; // in pixels - int width; // in pixels, account for fractional scaling - int height; // in pixels, account for fractional scaling - float dpi; - struct wl_output *wl_output; - int wld_scale; // Wayland scale factor - float gui_scale; // FLTK scale factor - bool done; - struct wl_list link; - }; - enum cursor_shapes {arrow = 0, wait, insert, hand, help, cross, move, - north, south, west, east, north_south, west_east, south_west, south_east, north_east, north_west, nesw, nwse}; - static const int cursor_count = nwse + 1; // nber of elements of 'enum cursor_shapes' - -// static member variables - static FL_EXPORT struct wl_display *wl_display; - static const struct wl_data_device_listener *p_data_device_listener; - // next length of marked text after current marked text will have been replaced - static int next_marked_length; - static compositor_name compositor; // identifies the used Wayland compositor - -// static member functions - static void insertion_point_location(int x, int y, int height); - static bool insertion_point_location(int *px, int *py, int *pwidth, int *pheight); - static bool own_output(struct wl_output *output); - static void do_set_cursor(struct Fl_Wayland_Screen_Driver::seat *, - struct wl_cursor *wl_cursor = NULL, Fl_Cursor c = FL_CURSOR_NONE); -// member variables - struct wl_cursor *xc_cursor[cursor_count]; // one for each element of enum cursor_shapes - struct wl_registry *wl_registry; - struct wl_compositor *wl_compositor; - struct wl_subcompositor *wl_subcompositor; - struct wl_shm *wl_shm; - struct seat *seat; - struct wl_list outputs; // linked list of struct output records for all screens in system - struct libdecor *libdecor_context; - struct xdg_wm_base *xdg_wm_base; - struct zwp_text_input_manager_v3 *text_input_base; -#if HAVE_XDG_DIALOG - struct xdg_wm_dialog_v1 *xdg_wm_dialog; -#endif -#if HAVE_CURSOR_SHAPE - struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager; - struct wp_cursor_shape_device_v1 *wp_cursor_shape_device; -#endif - -// constructor - Fl_Wayland_Screen_Driver(); - -// overridden functions from parent class Fl_Screen_Driver - APP_SCALING_CAPABILITY rescalable() FL_OVERRIDE { return PER_SCREEN_APP_SCALING; } - float scale(int n) FL_OVERRIDE; - void scale(int n, float f) FL_OVERRIDE; - // --- screen configuration - void init() FL_OVERRIDE; - int x() FL_OVERRIDE; - int y() FL_OVERRIDE; - int w() FL_OVERRIDE; - int h() FL_OVERRIDE; - void screen_xywh(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - void screen_dpi(float &h, float &v, int n=0) FL_OVERRIDE; - void screen_work_area(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - // --- audible output - void beep(int type) FL_OVERRIDE; - // --- global events - void flush() FL_OVERRIDE; - void grab(Fl_Window* win) FL_OVERRIDE; - // --- global colors - void get_system_colors() FL_OVERRIDE; - // this one is in fl_wayland_clipboard_dnd.cxx - int dnd(int unused) FL_OVERRIDE; - int compose(int &del) FL_OVERRIDE; - void compose_reset() FL_OVERRIDE; - Fl_RGB_Image *read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win, - bool may_capture_subwins, bool *did_capture_subwins) FL_OVERRIDE; - int get_mouse(int &x, int &y) FL_OVERRIDE; - void open_display_platform() FL_OVERRIDE; - void close_display() FL_OVERRIDE; - void display(const char *d) FL_OVERRIDE; - // --- compute dimensions of an Fl_Offscreen - void offscreen_size(Fl_Offscreen o, int &width, int &height) FL_OVERRIDE; - int has_marked_text() const FL_OVERRIDE; - // --- clipboard operations - // this one is in fl_wayland_clipboard_dnd.cxx - void copy(const char *stuff, int len, int clipboard, const char *type) FL_OVERRIDE; - // this one is in fl_wayland_clipboard_dnd.cxx - void paste(Fl_Widget &receiver, int clipboard, const char *type) FL_OVERRIDE; - // this one is in fl_wayland_clipboard_dnd.cxx - int clipboard_contains(const char *type) FL_OVERRIDE; - void set_spot(int font, int height, int x, int y, int w, int h, Fl_Window *win) FL_OVERRIDE; - void reset_spot() FL_OVERRIDE; - void *control_maximize_button(void *data) FL_OVERRIDE; - int event_key(int k) FL_OVERRIDE; - int get_key(int k) FL_OVERRIDE; - void enable_im() FL_OVERRIDE; - void disable_im() FL_OVERRIDE; - bool screen_boundaries_known() FL_OVERRIDE { return false; } - float base_scale(int numscreen) FL_OVERRIDE; - - // overridden functions from parent class Fl_Unix_Screen_Driver - int poll_or_select_with_delay(double time_to_wait) FL_OVERRIDE; - int poll_or_select() FL_OVERRIDE; - -// Wayland-specific member functions - void screen_count_set(int count) {num_screens = count;} - int screen_count_get() {return num_screens;} - void reset_cursor(); - // this one is in fl_wayland_clipboard_dnd.cxx - void copy_image(const unsigned char* data, int W, int H); - void init_workarea(); - void set_cursor(); - struct wl_cursor *default_cursor(); - void default_cursor(struct wl_cursor *cursor); - struct wl_cursor *cache_cursor(const char *cursor_name); - uint32_t get_serial(); - struct wl_seat *get_wl_seat(); - char *get_seat_name(); - struct xkb_keymap *get_xkb_keymap(); -}; - - -#endif // FL_WAYLAND_SCREEN_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx deleted file mode 100644 index 9199f3a5f..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx +++ /dev/null @@ -1,2204 +0,0 @@ -// -// Implementation of Wayland Screen interface -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include "../../Fl_Scalable_Graphics_Driver.H" -#include -#include "../../../libdecor/build/fl_libdecor.h" -#include "xdg-shell-client-protocol.h" -#include "../Posix/Fl_Posix_System_Driver.H" -#include -#include -#include -#include -#include -#include -#include "../../print_button.h" -#include -#include -#include -#include -#include -#include "text-input-client-protocol.h" -#include "gtk-shell-client-protocol.h" -#if HAVE_XDG_DIALOG -# include "xdg-dialog-client-protocol.h" -#endif -#if HAVE_CURSOR_SHAPE -# include "cursor-shape-client-protocol.h" -#endif -#include -#include -#include -#include -#include // for strerror() -#include -extern "C" { - bool libdecor_get_cursor_settings(char **theme, int *size); - bool fl_is_surface_from_GTK_titlebar (struct wl_surface *surface, struct libdecor_frame *frame, - bool *using_GTK); -} - -// set this to 1 for keyboard debug output, 0 for no debug output -#define DEBUG_KEYBOARD 0 - -#define fl_max(a,b) ((a) > (b) ? (a) : (b)) -#define fl_min(a,b) ((a) < (b) ? (a) : (b)) - -struct pointer_output { - Fl_Wayland_Screen_Driver::output* output; - struct wl_list link; -}; - -/* Implementation note: - -- About CSD and SSD : - * Mutter and Weston use CSD (client-side decoration) which means that libdecor.so draws all window - titlebars and responds to resize, minimization and maximization events. - * KWin uses SSD (server-side decoration) which means the OS draws titlebars according to its own rules - and triggers resize, minimization and maximization events. - -- Function registry_handle_global() runs within fl_open_display() and sets public static variable - Fl_Wayland_Screen_Driver::compositor to either Fl_Wayland_Screen_Driver::MUTTER, ::WESTON, or ::KWIN. - -- Specific operations for WESTON: - * When a libdecor-framed window is minimized under Weston, the frame remains on display. To avoid - that, function libdecor_frame_set_minimized() is modified so it turns off the frame's visibility, with - function libdecor_frame_set_visibility(), when the window is minimized. That's implemented in file - libdecor/build/fl_libdecor.c. The modified libdecor_frame_set_minimized() function, part of libdecor.so, - needs access to variable Fl_Wayland_Screen_Driver::compositor, part of libfltk.a. This is achieved - calling FLTK function fl_libdecor_using_weston() which returns whether the running compositor - is Weston. This Weston bug has been corrected in Weston version 10. Thus, this special processing - is not performed when Weston version is ≥ 10. - -- Support of Fl_Window::border(int) : - FLTK uses libdecor_frame_set_visibility() to show or hide a toplevel window's frame. This doesn't work - with KWin which uses Server-Side Decoration. In that case, FLTK hides and re-shows the window to toggle - between presence and absence of a window's frame. -*/ - - -static std::vector key_vector; // used by Fl_Wayland_Screen_Driver::event_key() -static struct wl_surface *gtk_shell_surface = NULL; - -Fl_Wayland_Screen_Driver::compositor_name Fl_Wayland_Screen_Driver::compositor = - Fl_Wayland_Screen_Driver::unspecified; - - -extern "C" { - bool fl_libdecor_using_weston(void) { - return Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON; - } -} - - -static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) -{ - xdg_wm_base_pong(xdg_wm_base, serial); -} - - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = xdg_wm_base_ping, -}; - - -// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx -extern const char *fl_fg; -extern const char *fl_bg; -extern const char *fl_bg2; -// end of extern additions workaround - - -void Fl_Wayland_Screen_Driver::do_set_cursor( - struct Fl_Wayland_Screen_Driver::seat *seat, struct wl_cursor *wl_cursor, Fl_Cursor cursor) { - /* - wl_cursor: when non-NULL means a custom cursor; - when NULL: - - with "Cursor shape" protocol, cursor is meaningful if != FL_CURSOR_NONE; - - with old-school cursors, seat->default_cursor gives the desired cursor. - cursor: used with "Cursor shape" protocol for enumerated cursor shape, otherwise equal to FL_CURSOR_NONE - */ - struct wl_cursor_image *image; - struct wl_buffer *buffer; - const int scale = seat->pointer_scale; - -#if HAVE_CURSOR_SHAPE - static std::map cursor_shape_map = { - {FL_CURSOR_DEFAULT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT }, - {FL_CURSOR_ARROW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT }, - {FL_CURSOR_CROSS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR }, - {FL_CURSOR_WAIT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT }, - {FL_CURSOR_INSERT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT }, - {FL_CURSOR_HAND, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB }, - {FL_CURSOR_HELP, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP }, - {FL_CURSOR_MOVE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE }, - {FL_CURSOR_N, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE }, - {FL_CURSOR_E, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE }, - {FL_CURSOR_W, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE }, - {FL_CURSOR_S, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE }, - {FL_CURSOR_NS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE }, - {FL_CURSOR_WE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE }, - {FL_CURSOR_SW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE }, - {FL_CURSOR_SE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE }, - {FL_CURSOR_NE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE }, - {FL_CURSOR_NW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE }, - {FL_CURSOR_NESW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE }, - {FL_CURSOR_NWSE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE } - }; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->wp_cursor_shape_device && !wl_cursor) { - if (cursor != FL_CURSOR_NONE) wp_cursor_shape_device_v1_set_shape( - scr_driver->wp_cursor_shape_device, seat->pointer_enter_serial, cursor_shape_map[cursor]); - return; - } -#endif - - if ((!seat->cursor_theme && !wl_cursor) || !seat->wl_pointer) - return; - - if (!wl_cursor) wl_cursor = seat->default_cursor; - image = wl_cursor->images[0]; - buffer = wl_cursor_image_get_buffer(image); - wl_pointer_set_cursor(seat->wl_pointer, seat->pointer_enter_serial, - seat->cursor_surface, - image->hotspot_x / scale, - image->hotspot_y / scale); - wl_surface_attach(seat->cursor_surface, buffer, 0, 0); - wl_surface_set_buffer_scale(seat->cursor_surface, scale); - wl_surface_damage_buffer(seat->cursor_surface, 0, 0, - image->width, image->height); - wl_surface_commit(seat->cursor_surface); -} - - -static uint32_t ptime; -static uint32_t wld_event_time; -static int px, py; - - -static void set_event_xy(Fl_Window *win) { - // turn off is_click if enough time or mouse movement has passed: - if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 || - wld_event_time >= ptime+1000) { - Fl::e_is_click = 0; -//fprintf(stderr, "Fl::e_is_click = 0\n"); - } -} - - -// if this is same event as last && is_click, increment click count: -static inline void checkdouble() { - if (Fl::e_is_click == Fl::e_keysym) { - Fl::e_clicks++; -//fprintf(stderr, "Fl::e_clicks = %d\n", Fl::e_clicks); - } else { - Fl::e_clicks = 0; - Fl::e_is_click = Fl::e_keysym; -//fprintf(stderr, "Fl::e_is_click = %d\n", Fl::e_is_click); - } - px = Fl::e_x_root; - py = Fl::e_y_root; - ptime = wld_event_time; -} - - -struct wl_display *Fl_Wayland_Screen_Driver::wl_display = NULL; - - -static Fl_Window *event_coords_from_surface(struct wl_surface *surface, - wl_fixed_t surface_x, wl_fixed_t surface_y) { - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - if (!win) return NULL; - int delta_x = 0, delta_y = 0; - while (win->parent()) { - delta_x += win->x(); - delta_y += win->y(); - win = win->window(); - } - float f = Fl::screen_scale(win->screen_num()); - Fl::e_x = wl_fixed_to_int(surface_x) / f + delta_x; - Fl::e_x_root = Fl::e_x + win->x(); - Fl::e_y = wl_fixed_to_int(surface_y) / f + delta_y; - int *poffset = Fl_Window_Driver::menu_offset_y(win); - if (poffset) Fl::e_y -= *poffset; - Fl::e_y_root = Fl::e_y + win->y(); - return win; -} - -static Fl_Window *need_leave = NULL; - -static void pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, - struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - Fl_Window *win = event_coords_from_surface(surface, surface_x, surface_y); - static bool using_GTK = seat->gtk_shell && - (gtk_shell1_get_version(seat->gtk_shell) >= GTK_SURFACE1_TITLEBAR_GESTURE_SINCE_VERSION); - if (!win && using_GTK) { - // check whether surface is the headerbar of a GTK-decorated window - Fl_X *xp = Fl_X::first; - while (xp && using_GTK) { // all mapped windows - struct wld_window *xid = (struct wld_window*)xp->xid; - if (xid->kind == Fl_Wayland_Window_Driver::DECORATED && - fl_is_surface_from_GTK_titlebar(surface, xid->frame, &using_GTK)) { - gtk_shell_surface = surface; - break; - } - xp = xp->next; - } - } - if (!win) return; - //fprintf(stderr, "pointer_enter window=%p\n", Fl_Wayland_Window_Driver::surface_to_window(surface)); - seat->pointer_focus = surface; - // use custom cursor if present - struct wl_cursor *cursor = - fl_wl_xid(win)->custom_cursor ? fl_wl_xid(win)->custom_cursor->wl_cursor : NULL; - seat->serial = serial; - seat->pointer_enter_serial = serial; - Fl_Wayland_Screen_Driver::do_set_cursor(seat, cursor, Fl_Wayland_Window_Driver::driver(win)->standard_cursor()); - set_event_xy(win); - need_leave = NULL; - win = Fl_Wayland_Window_Driver::surface_to_window(surface); - // Caution: with an Fl_Tooltip this call can hide the window being entered (#1317) - if (!win->parent()) Fl::handle(FL_ENTER, win); -} - - -static void pointer_leave(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - if (seat->pointer_focus == surface) seat->pointer_focus = NULL; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - gtk_shell_surface = NULL; - if (win) { - //fprintf(stderr, "pointer_leave window=%p [%s]\n", win, (win->parent()?"sub":"top")); - set_event_xy(win); - need_leave = win->top_window(); // we leave a sub or toplevel window - wl_display_roundtrip(fl_wl_display()); // pointer_enter to other win, if applicable, will run - if (need_leave) { // we really left the sub-or-top win and did not enter another - extern Fl_Window *fl_xmousewin; - fl_xmousewin = 0; - Fl::handle(FL_LEAVE, need_leave); - } - } -} - - -static void pointer_motion(void *data, struct wl_pointer *wl_pointer, - uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - Fl_Window *win = event_coords_from_surface(seat->pointer_focus, surface_x, surface_y); - if (!win) return; - if (Fl::grab() && !Fl::grab()->menu_window() && Fl::grab() != win) { - // If there's an active, non-menu grab() and the pointer is in a window other than - // the grab(), make e_x_root too large to be in any window - Fl::e_x_root = 1000000; - } - else if (Fl_Window_Driver::menu_parent(NULL) && // any kind of menu is active now, and - !win->menu_window() && // we enter a non-menu window - win != Fl_Window_Driver::menu_parent(NULL) // that's not the window below the menu - ) { - Fl::e_x_root = 1000000; // make it too large to be in any window - } -//fprintf(stderr, "FL_MOVE on win=%p to x:%dx%d root:%dx%d\n", win, Fl::e_x, Fl::e_y, Fl::e_x_root, Fl::e_y_root); - wld_event_time = time; - set_event_xy(win); - Fl::handle(FL_MOVE, win); -} - - -//#include -static void pointer_button(void *data, - struct wl_pointer *wl_pointer, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) -{ - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - if (gtk_shell_surface && state == WL_POINTER_BUTTON_STATE_PRESSED && - button == BTN_MIDDLE) { - struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(seat->gtk_shell,gtk_shell_surface); - gtk_surface1_titlebar_gesture(gtk_surface, serial, seat->wl_seat, - GTK_SURFACE1_GESTURE_MIDDLE_CLICK); - gtk_surface1_release(gtk_surface); // very necessary - return; - } - seat->serial = serial; - int event = 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus); - if (!win) return; - win = win->top_window(); - wld_event_time = time; - int b = 0; - // Fl::e_state &= ~FL_BUTTONS; // DO NOT reset the mouse button state! - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - if (button == BTN_LEFT) { Fl::e_state |= FL_BUTTON1; b = 1; } - else if (button == BTN_RIGHT) { Fl::e_state |= FL_BUTTON3; b = 3; } - else if (button == BTN_MIDDLE) { Fl::e_state |= FL_BUTTON2; b = 2; } - else if (button == BTN_BACK) { Fl::e_state |= FL_BUTTON4; b = 4; } // ? - else if (button == BTN_SIDE) { Fl::e_state |= FL_BUTTON4; b = 4; } // OK: Debian 12 - else if (button == BTN_FORWARD) { Fl::e_state |= FL_BUTTON5; b = 5; } // ? - else if (button == BTN_EXTRA) { Fl::e_state |= FL_BUTTON5; b = 5; } // OK: Debian 12 - } else { // must be WL_POINTER_BUTTON_STATE_RELEASED - if (button == BTN_LEFT) { Fl::e_state &= ~FL_BUTTON1; b = 1; } - else if (button == BTN_RIGHT) { Fl::e_state &= ~FL_BUTTON3; b = 3; } - else if (button == BTN_MIDDLE) { Fl::e_state &= ~FL_BUTTON2; b = 2; } - else if (button == BTN_BACK) { Fl::e_state &= ~FL_BUTTON4; b = 4; } // ? - else if (button == BTN_SIDE) { Fl::e_state &= ~FL_BUTTON4; b = 4; } // OK: Debian 12 - else if (button == BTN_FORWARD) { Fl::e_state &= ~FL_BUTTON5; b = 5; } // ? - else if (button == BTN_EXTRA) { Fl::e_state &= ~FL_BUTTON5; b = 5; } // OK: Debian 12 - } - Fl::e_keysym = FL_Button + b; - Fl::e_dx = Fl::e_dy = 0; - - set_event_xy(win); - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - event = FL_PUSH; - checkdouble(); - } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) { - event = FL_RELEASE; - } - // fprintf(stderr, "%s %s\n", fl_eventnames[event], win->label() ? win->label():"[]"); - Fl::handle(event, win); -} - - -static void pointer_axis(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus); - if (!win) return; - wld_event_time = time; - int delta = wl_fixed_to_int(value); - if (abs(delta) >= 10) delta /= 10; - // fprintf(stderr, "FL_MOUSEWHEEL: %c delta=%d\n", axis==WL_POINTER_AXIS_HORIZONTAL_SCROLL?'H':'V', delta); - // allow both horizontal and vertical movements to be processed by the widget - if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { - if (Fl::event_shift()) { // shift key pressed: send vertical mousewheel event - Fl::e_dx = 0; - Fl::e_dy = delta; - } else { // shift key not pressed (normal behavior): send horizontal mousewheel event - Fl::e_dx = delta; - Fl::e_dy = 0; - } - Fl::handle(FL_MOUSEWHEEL, win->top_window()); - } - if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - if (Fl::event_shift()) { // shift key pressed: send horizontal mousewheel event - Fl::e_dx = delta; - Fl::e_dy = 0; - } else {// shift key not pressed (normal behavior): send vertical mousewheel event - Fl::e_dx = 0; - Fl::e_dy = delta; - } - Fl::handle(FL_MOUSEWHEEL, win->top_window()); - } -} - - -static struct wl_pointer_listener pointer_listener = { - pointer_enter, - pointer_leave, - pointer_motion, - pointer_button, - pointer_axis -}; - - -static const char *proxy_tag = "FLTK for Wayland"; - - -bool Fl_Wayland_Screen_Driver::own_output(struct wl_output *output) -{ - return wl_proxy_get_tag((struct wl_proxy *)output) == &proxy_tag; -} - - -static void init_cursors(struct Fl_Wayland_Screen_Driver::seat *seat); - - -static void try_update_cursor(struct Fl_Wayland_Screen_Driver::seat *seat) { - if (wl_list_empty(&seat->pointer_outputs)) return; - struct pointer_output *pointer_output; - int scale = 1; - - wl_list_for_each(pointer_output, &seat->pointer_outputs, link) { - scale = fl_max(scale, pointer_output->output->wld_scale); - } - - if (scale != seat->pointer_scale) { - seat->pointer_scale = scale; - init_cursors(seat); - Fl_Wayland_Screen_Driver::do_set_cursor(seat); - } -} - - -static void output_scale(void *data, struct wl_output *wl_output, int32_t factor); - - -static void cursor_surface_enter(void *data, - struct wl_surface *wl_surface, struct wl_output *wl_output) { - // Runs when the seat's cursor_surface enters a display - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - struct pointer_output *pointer_output; - - if (!Fl_Wayland_Screen_Driver::own_output(wl_output)) - return; - - pointer_output = (struct pointer_output *)calloc(1, sizeof(struct pointer_output)); - pointer_output->output = - (Fl_Wayland_Screen_Driver::output *)wl_output_get_user_data(wl_output); -//fprintf(stderr, "cursor_surface_enter: wl_output_get_user_data(%p)=%p\n", wl_output, pointer_output->output); - wl_list_insert(&seat->pointer_outputs, &pointer_output->link); - try_update_cursor(seat); - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output); - output_scale(output, wl_output, output->wld_scale); // rescale custom cursors - // maintain custom or standard window cursor - Fl_Window *win = Fl::first_window(); - if (win) { - Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win); - struct wld_window *xid = fl_wl_xid(win); - if (xid->custom_cursor) Fl_Wayland_Screen_Driver::do_set_cursor(seat, xid->custom_cursor->wl_cursor); - else if (driver->cursor_default()) driver->set_cursor(driver->cursor_default()); - else win->cursor(driver->standard_cursor()); - } -} - - -static void cursor_surface_leave(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - struct pointer_output *pointer_output, *tmp; - wl_list_for_each_safe(pointer_output, tmp, &seat->pointer_outputs, link) { - if (pointer_output->output->wl_output == wl_output) { - wl_list_remove(&pointer_output->link); - free(pointer_output); - } - } - try_update_cursor(seat); - // maintain custom window cursor - Fl_Window *win = Fl::first_window(); - if (win) { - struct wld_window *xid = fl_wl_xid(win); - if (xid->custom_cursor) Fl_Wayland_Screen_Driver::do_set_cursor(seat, xid->custom_cursor->wl_cursor); - } -} - - -static struct wl_surface_listener cursor_surface_listener = { - cursor_surface_enter, - cursor_surface_leave, -}; - - -static void init_cursors(struct Fl_Wayland_Screen_Driver::seat *seat) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (!seat->cursor_surface) { - seat->cursor_surface = wl_compositor_create_surface(scr_driver->wl_compositor); - wl_surface_add_listener(seat->cursor_surface, &cursor_surface_listener, seat); - } -#if HAVE_CURSOR_SHAPE - if (scr_driver->wp_cursor_shape_manager) return; -#endif - - char *name; - int size; - struct wl_cursor_theme *theme; - - if (!libdecor_get_cursor_settings(&name, &size)) { - name = NULL; - size = 24; - } - size *= seat->pointer_scale; - theme = wl_cursor_theme_load(name, size, scr_driver->wl_shm); - free(name); - if (theme != NULL) { - if (seat->cursor_theme) { - // caution to destroy theme because Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor) caches used cursors - scr_driver->reset_cursor(); - wl_cursor_theme_destroy(seat->cursor_theme); - } - seat->cursor_theme = theme; - } - if (seat->cursor_theme) { - seat->default_cursor = scr_driver->xc_cursor[Fl_Wayland_Screen_Driver::arrow] = - wl_cursor_theme_get_cursor(seat->cursor_theme, "left_ptr"); - } -} - - -static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, - uint32_t format, int32_t fd, uint32_t size) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); - - char *map_shm = (char*)mmap(NULL, size, PROT_READ, - wl_keyboard_get_version(wl_keyboard) >= 7 ? MAP_PRIVATE : MAP_SHARED, fd, 0); - assert(map_shm != MAP_FAILED); - - struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(seat->xkb_context, map_shm, - XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); - munmap(map_shm, size); - close(fd); - if (xkb_keymap) { - struct xkb_state *xkb_state = xkb_state_new(xkb_keymap); - xkb_keymap_unref(seat->xkb_keymap); - if (seat->xkb_state) xkb_state_unref(seat->xkb_state); - seat->xkb_keymap = xkb_keymap; - seat->xkb_state = xkb_state; - } -} - - -static int search_int_vector(std::vector& v, int val) { - for (unsigned pos = 0; pos < v.size(); pos++) { - if (v[pos] == val) return pos; - } - return -1; -} - - -static void remove_int_vector(std::vector& v, int val) { - int pos = search_int_vector(v, val); - if (pos < 0) return; - v.erase(v.begin()+pos); -} - - -static int process_wld_key(struct xkb_state *xkb_state, uint32_t key, - uint32_t *p_keycode, xkb_keysym_t *p_sym) { - uint32_t keycode = key + 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, keycode); - if (sym == 0xfe20) sym = FL_Tab; - if (sym == 0xffeb) sym = FL_Meta_L; // repair value libxkb gives for FL_Meta_L - if (sym == 0xffec) sym = FL_Meta_R; // repair value libxkb gives for FL_Meta_R - if (sym >= 'A' && sym <= 'Z') sym += 32; // replace uppercase by lowercase letter - int for_key_vector = sym; // for support of Fl::event_key(int) - // special processing for number keys == keycodes 10-19 : - if (keycode >= 10 && keycode <= 18) { - for_key_vector = '1' + (keycode - 10); - } else if (keycode == 19) { - for_key_vector = '0'; - } - if (p_keycode) *p_keycode = keycode; - if (p_sym) *p_sym = sym; - return for_key_vector; -} - - -static uint32_t last_keydown_serial = 0; // serial of last keydown event - - -static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; -//fprintf(stderr, "keyboard enter fl_win=%p; keys pressed are: ", Fl_Wayland_Window_Driver::surface_to_window(surface)); - key_vector.clear(); - // Replace wl_array_for_each(p, keys) rejected by C++ - for (uint32_t *p = (uint32_t *)(keys)->data; - (const char *) p < ((const char *) (keys)->data + (keys)->size); - (p)++) { - int for_key_vector = process_wld_key(seat->xkb_state, *p, NULL, NULL); -//fprintf(stderr, "%d ", for_key_vector); - if (search_int_vector(key_vector, for_key_vector) < 0) { - key_vector.push_back(for_key_vector); - } - } -//fprintf(stderr, "\n"); - seat->keyboard_surface = surface; - seat->keyboard_enter_serial = serial; - last_keydown_serial = 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - if (win) { - Fl::handle(FL_FOCUS, win); - fl_wl_find(fl_wl_xid(win)); - } -} - - -struct key_repeat_data_t { - uint32_t serial; - Fl_Window *window; -}; - -#define KEY_REPEAT_DELAY 0.5 // sec -#define KEY_REPEAT_INTERVAL 0.05 // sec - - -static void key_repeat_timer_cb(key_repeat_data_t *key_repeat_data) { - if (last_keydown_serial == key_repeat_data->serial) { - Fl::handle(FL_KEYDOWN, key_repeat_data->window); - Fl::add_timeout(KEY_REPEAT_INTERVAL, (Fl_Timeout_Handler)key_repeat_timer_cb, key_repeat_data); - } - else delete key_repeat_data; -} - - -int Fl_Wayland_Screen_Driver::next_marked_length = 0; - - -int Fl_Wayland_Screen_Driver::has_marked_text() const { - return 1; -} - - -int Fl_Wayland_Screen_Driver::insertion_point_x = 0; -int Fl_Wayland_Screen_Driver::insertion_point_y = 0; -int Fl_Wayland_Screen_Driver::insertion_point_width = 0; -int Fl_Wayland_Screen_Driver::insertion_point_height = 0; -bool Fl_Wayland_Screen_Driver::insertion_point_location_is_valid = false; - -static int previous_cursor_x = 0, previous_cursor_y = 0, previous_cursor_h = 0; -static uint32_t commit_serial = 0; -static char *current_pre_edit = NULL; -static char *pending_pre_edit = NULL; -static char *pending_commit = NULL; - - -static void send_commit(struct zwp_text_input_v3 *zwp_text_input_v3) { - zwp_text_input_v3_commit(zwp_text_input_v3); - commit_serial++; -} - - -// inform TIM about location of the insertion point, and memorize this info. -void Fl_Wayland_Screen_Driver::insertion_point_location(int x, int y, int height) { -//printf("insertion_point_location %dx%d\n",x,y); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->seat->text_input /*&& !current_pre_edit*/ && - (x != previous_cursor_x || y != previous_cursor_y || height != previous_cursor_h)) { - previous_cursor_x = x; - previous_cursor_y = y; - previous_cursor_h = height; - if (Fl::focus()) { - Fl_Widget *focuswin = Fl::focus()->window(); - while (focuswin && focuswin->parent()) { - x += focuswin->x(); y += focuswin->y(); - focuswin = focuswin->window(); - } - } - float s = fl_graphics_driver->scale(); - insertion_point_location_is_valid = true; - insertion_point_x = s*x; - insertion_point_y = s*(y-height); - insertion_point_width = s*5; - insertion_point_height = s*height; - if (zwp_text_input_v3_get_user_data(scr_driver->seat->text_input) ) { - zwp_text_input_v3_set_cursor_rectangle(scr_driver->seat->text_input, - insertion_point_x, insertion_point_y, - insertion_point_width, insertion_point_height); - send_commit(scr_driver->seat->text_input); - } - } -} - - -// computes window coordinates & size of insertion point -bool Fl_Wayland_Screen_Driver::insertion_point_location(int *px, int *py, - int *pwidth, int *pheight) { - // return true if the current coordinates and size of the insertion point are available - if ( ! insertion_point_location_is_valid ) return false; - *px = insertion_point_x; - *py = insertion_point_y; - *pwidth = insertion_point_width; - *pheight = insertion_point_height; - return true; -} - - -int Fl_Wayland_Screen_Driver::compose(int& del) { - unsigned char ascii = (unsigned char)Fl::e_text[0]; - // letter+modifier key - int condition = (Fl::e_state & (FL_ALT | FL_META | FL_CTRL)) && ascii < 128 ; - // pressing modifier key - // FL_Shift_L, FL_Shift_R, FL_Control_L, FL_Control_R, FL_Caps_Lock - // FL_Meta_L, FL_Meta_R, FL_Alt_L, FL_Alt_R - condition |= ((Fl::e_keysym >= FL_Shift_L && Fl::e_keysym <= FL_Alt_R) || - Fl::e_keysym == FL_Alt_Gr); - // FL_Home FL_Left FL_Up FL_Right FL_Down FL_Page_Up FL_Page_Down FL_End - // FL_Print FL_Insert FL_Menu FL_Help and more - condition |= (Fl::e_keysym >= FL_Home && Fl::e_keysym <= FL_Num_Lock); - condition |= (Fl::e_keysym >= FL_F && Fl::e_keysym <= FL_F_Last); - condition |= Fl::e_keysym == FL_Tab || Fl::e_keysym == FL_Scroll_Lock || Fl::e_keysym == FL_Pause; -//fprintf(stderr, "compose: condition=%d e_state=%x ascii=%d\n", condition, Fl::e_state, ascii); - if (condition) { del = 0; return 0;} -//fprintf(stderr, "compose: del=%d compose_state=%d next_marked_length=%d \n", del, Fl::compose_state, next_marked_length); - del = Fl::compose_state; - Fl::compose_state = next_marked_length; - // no-underlined-text && (ascii non-printable || ascii == delete) - if (ascii && (!Fl::compose_state) && (ascii <= 31 || ascii == 127)) { del = 0; return 0; } - return 1; -} - - -void Fl_Wayland_Screen_Driver::compose_reset() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); - Fl::compose_state = 0; - next_marked_length = 0; - if (seat->xkb_compose_state) xkb_compose_state_reset(seat->xkb_compose_state); -} - - -struct dead_key_struct { - xkb_keysym_t keysym; // the keysym obtained when hitting a dead key - const char *marked_text; // the temporary text to display for that dead key -}; - - -static dead_key_struct dead_keys[] = { - {XKB_KEY_dead_grave, "`"}, - {XKB_KEY_dead_acute, "´"}, - {XKB_KEY_dead_circumflex, "^"}, - {XKB_KEY_dead_tilde, "~"}, - {XKB_KEY_dead_macron, "¯"}, - {XKB_KEY_dead_breve, "˘"}, - {XKB_KEY_dead_abovedot, "˙"}, - {XKB_KEY_dead_diaeresis, "¨"}, - {XKB_KEY_dead_abovering, "˚"}, - {XKB_KEY_dead_doubleacute, "˝"}, - {XKB_KEY_dead_caron, "ˇ"}, - {XKB_KEY_dead_cedilla, "¸"}, - {XKB_KEY_dead_ogonek, "˛"}, - {XKB_KEY_dead_iota, "ι"}, - {XKB_KEY_dead_doublegrave, " ̏"}, -}; - - -const int dead_key_count = sizeof(dead_keys)/sizeof(struct dead_key_struct); - -static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - seat->serial = serial; - static char buf[128]; - uint32_t keycode; - xkb_keysym_t sym; - int for_key_vector = process_wld_key(seat->xkb_state, key, &keycode, &sym); -#if (DEBUG_KEYBOARD) - xkb_keysym_get_name(sym, buf, sizeof(buf)); - const char *action = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release"); - fprintf(stderr, "wl_keyboard_key: key %s: sym: %-12s(%d) code:%u fl_win=%p, ", - action, buf, sym, keycode, - Fl_Wayland_Window_Driver::surface_to_window(seat->keyboard_surface)); -#endif - xkb_state_key_get_utf8(seat->xkb_state, keycode, buf, sizeof(buf)); -#if (DEBUG_KEYBOARD) - fprintf(stderr, "utf8: '%s' e_length=%d [%d]\n", buf, (int)strlen(buf), *buf); -#endif - Fl::e_keysym = Fl::e_original_keysym = for_key_vector; - if (!(Fl::e_state & FL_NUM_LOCK) && sym >= XKB_KEY_KP_Home && sym <= XKB_KEY_KP_Delete) { - // compute e_keysym and e_original_keysym for keypad number keys and '.|,' when NumLock is off - static const int table[11] = {FL_Home /* 7 */, FL_Left /* 4 */, FL_Up /* 8 */, - FL_Right /* 6 */, FL_Down /* 2 */, FL_Page_Up /* 9 */, - FL_Page_Down /* 3 */, FL_End /* 1 */, 0xff0b /* 5 */, - FL_Insert /* 0 */, FL_Delete /* .|, */}; - static const int table_original[11] = {0xffb7 /* 7 */, 0xffb4 /* 4 */, 0xffb8 /* 8 */, - 0xffb6 /* 6 */, 0xffb2 /* 2 */, 0xffb9 /* 9 */, - 0xffb3 /* 3 */, 0xffb1 /* 1 */, 0xffb5 /* 5 */, - 0xffb0 /* 0 */, 0xffac /* .|, */}; - Fl::e_keysym = table[sym - XKB_KEY_KP_Home]; - Fl::e_original_keysym = table_original[sym - XKB_KEY_KP_Home]; - for_key_vector = Fl::e_original_keysym; - } -#if (DEBUG_KEYBOARD) - fprintf(stderr, "wl_keyboard_key: e_keysym=%x e_original_keysym=%x\n", Fl::e_keysym, Fl::e_original_keysym); -#endif - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if (search_int_vector(key_vector, for_key_vector) < 0) { - key_vector.push_back(for_key_vector); - } - } else { - last_keydown_serial = 0; - remove_int_vector(key_vector, for_key_vector); - } - Fl::e_text = buf; - Fl::e_length = (int)strlen(buf); - // Process dead keys and compose sequences : - enum xkb_compose_status status = XKB_COMPOSE_NOTHING; - // This part is useful only if the compositor doesn't support protocol text-input-unstable-v3 - if (seat->xkb_compose_state && state == WL_KEYBOARD_KEY_STATE_PRESSED && - !(sym >= FL_Shift_L && sym <= FL_Alt_R) && sym != XKB_KEY_ISO_Level3_Shift) { - xkb_compose_state_feed(seat->xkb_compose_state, sym); - status = xkb_compose_state_get_status(seat->xkb_compose_state); - if (status == XKB_COMPOSE_COMPOSING) { - if (Fl::e_length == 0) { // dead keys produce e_length = 0 - int i; - for (i = 0; i < dead_key_count; i++) { - if (dead_keys[i].keysym == sym) break; - } - if (i < dead_key_count) strcpy(buf, dead_keys[i].marked_text); - else buf[0] = 0; - Fl::e_length = (int)strlen(buf); - Fl::compose_state = 0; - } - Fl_Wayland_Screen_Driver::next_marked_length = Fl::e_length; - } else if (status == XKB_COMPOSE_COMPOSED) { - Fl::e_length = xkb_compose_state_get_utf8(seat->xkb_compose_state, buf, sizeof(buf)); - Fl::compose_state = Fl_Wayland_Screen_Driver::next_marked_length; - Fl_Wayland_Screen_Driver::next_marked_length = 0; - } else if (status == XKB_COMPOSE_CANCELLED) { - Fl::e_length = 0; - Fl::compose_state = Fl_Wayland_Screen_Driver::next_marked_length; - Fl_Wayland_Screen_Driver::next_marked_length = 0; - } -//fprintf(stderr, "xkb_compose_status=%d ctxt=%p state=%p l=%d[%s]\n", status, seat->xkb_context, seat->xkb_compose_state, Fl::e_length, buf); - } - // end of part used only without text-input-unstable-v3 - - wld_event_time = time; - int event = (state == WL_KEYBOARD_KEY_STATE_PRESSED ? FL_KEYDOWN : FL_KEYUP); - // Send event to focus-containing top window as defined by FLTK, - // otherwise send it to Wayland-defined focus window - Fl_Window *win = ( Fl::focus() ? Fl::focus()->top_window() : - Fl_Wayland_Window_Driver::surface_to_window(seat->keyboard_surface) ); - if (win) { - set_event_xy(win); - Fl::e_is_click = 0; - Fl::handle(event, win); - } - if (event == FL_KEYDOWN && status == XKB_COMPOSE_NOTHING && - !(sym >= FL_Shift_L && sym <= FL_Alt_R)) { - // Handling of key repeats : - // Use serial argument rather than time to detect repeated keys because - // serial value changes at each key up or down in all tested OS and compositors, - // whereas time value changes in Ubuntu24.04 KDE/Plasma 5.27.11 and Ubuntu22.04 KDE/Plasma 5.24.7 - // but not in Debian-testing KDE/Plasma 5.27.10. - // Unexplained difference in behaviors of KDE/Plasma compositor: - // Consider KDE settings -> input -> keyboard -> when a key is held: repeat/do nothing. - // This setting (repeat) has key-down wayland events repeated when key is held under Debian/KDE - // but not under Ubuntu/KDE ! - key_repeat_data_t *key_repeat_data = new key_repeat_data_t; - key_repeat_data->serial = serial; - key_repeat_data->window = win; - last_keydown_serial = serial; - Fl::add_timeout(KEY_REPEAT_DELAY, (Fl_Timeout_Handler)key_repeat_timer_cb, - key_repeat_data); - } -} - - -static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, struct wl_surface *surface) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; -//fprintf(stderr, "keyboard leave fl_win=%p\n", Fl_Wayland_Window_Driver::surface_to_window(surface)); - seat->keyboard_surface = NULL; - last_keydown_serial = 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); - if (!win && Fl::focus()) win = Fl::focus()->top_window(); - if (win) Fl::handle(FL_UNFOCUS, win); - key_vector.clear(); -} - - -static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, - uint32_t group) { - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - xkb_state_update_mask(seat->xkb_state, mods_depressed, mods_latched, mods_locked, - 0, 0, group); - Fl::e_state &= ~(FL_SHIFT+FL_CTRL+FL_ALT+FL_META+FL_CAPS_LOCK+FL_NUM_LOCK); - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_SHIFT, - XKB_STATE_MODS_DEPRESSED)) Fl::e_state |= FL_SHIFT; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CTRL, - XKB_STATE_MODS_DEPRESSED)) Fl::e_state |= FL_CTRL; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_ALT, - XKB_STATE_MODS_DEPRESSED)) Fl::e_state |= FL_ALT; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_LOGO, - XKB_STATE_MODS_DEPRESSED)) Fl::e_state |= FL_META; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_CAPS, - XKB_STATE_MODS_LOCKED)) Fl::e_state |= FL_CAPS_LOCK; - if (xkb_state_mod_name_is_active(seat->xkb_state, XKB_MOD_NAME_NUM, - XKB_STATE_MODS_LOCKED)) Fl::e_state |= FL_NUM_LOCK; -//fprintf(stderr, "mods_depressed=%u Fl::e_state=%X\n", mods_depressed, Fl::e_state); -} - - -static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) -{ - // wl_keyboard is version 3 under Debian, but that event isn't sent until version 4 -} - - -static const struct wl_keyboard_listener wl_keyboard_listener = { - .keymap = wl_keyboard_keymap, - .enter = wl_keyboard_enter, - .leave = wl_keyboard_leave, - .key = wl_keyboard_key, - .modifiers = wl_keyboard_modifiers, - .repeat_info = wl_keyboard_repeat_info, -}; - - -void text_input_enter(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, - struct wl_surface *surface) { -//puts("text_input_enter"); - zwp_text_input_v3_set_user_data(zwp_text_input_v3, surface); - zwp_text_input_v3_enable(zwp_text_input_v3); - zwp_text_input_v3_set_content_type(zwp_text_input_v3, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL); - int x, y, width, height; - if (Fl_Wayland_Screen_Driver::insertion_point_location(&x, &y, &width, &height)) { - zwp_text_input_v3_set_cursor_rectangle(zwp_text_input_v3, x, y, width, height); - } - send_commit(zwp_text_input_v3); -} - - -void text_input_leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, - struct wl_surface *surface) { -//puts("text_input_leave"); - zwp_text_input_v3_disable(zwp_text_input_v3); - zwp_text_input_v3_set_user_data(zwp_text_input_v3, NULL); - send_commit(zwp_text_input_v3); - free(pending_pre_edit); pending_pre_edit = NULL; - free(current_pre_edit); current_pre_edit = NULL; - free(pending_commit); pending_commit = NULL; -} - - -static void send_text_to_fltk(const char *text, bool is_marked, struct wl_surface *current_surface) { -//printf("send_text_to_fltk(%s, %d)\n",text,is_marked); - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(current_surface); - Fl::e_text = text ? (char*)text : (char*)""; - Fl::e_length = text ? (int)strlen(text) : 0; - Fl::e_keysym = 'a'; // fake a simple key - set_event_xy(win); - Fl::e_is_click = 0; - if (is_marked) { // goes to widget as marked text - Fl_Wayland_Screen_Driver::next_marked_length = Fl::e_length; - Fl::handle(FL_KEYDOWN, win); - } else if (text) { - Fl_Wayland_Screen_Driver::next_marked_length = 0; - Fl::handle(FL_KEYDOWN, win); - Fl::compose_state = 0; - } else { - Fl_Wayland_Screen_Driver::next_marked_length = 0; - Fl::handle(FL_KEYDOWN, win); - } -} - - -void text_input_preedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, - const char *text, int32_t cursor_begin, int32_t cursor_end) { -//printf("text_input_preedit_string %s cursor_begin=%d cursor_end=%d\n",text, cursor_begin, cursor_end); - free(pending_pre_edit); - pending_pre_edit = text ? strdup(text) : NULL; -} - - -void text_input_commit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, - const char *text) { -//printf("text_input_commit_string %s\n",text); - free(pending_commit); - pending_commit = (text ? strdup(text) : NULL); -} - - -void text_input_delete_surrounding_text(void *data, - struct zwp_text_input_v3 *zwp_text_input_v3, - uint32_t before_length, uint32_t after_length) { - fprintf(stderr, "delete_surrounding_text before=%d adfter=%d\n", - before_length,after_length); -} - - -void text_input_done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, - uint32_t serial) { -//puts("text_input_done"); - struct wl_surface *current_surface = (struct wl_surface*)data; - const bool bad_event = (serial != commit_serial); - if ((pending_pre_edit == NULL && current_pre_edit == NULL) || - (pending_pre_edit && current_pre_edit && strcmp(pending_pre_edit, current_pre_edit) == 0)) { - free(pending_pre_edit); pending_pre_edit = NULL; - } else { - free(current_pre_edit); - current_pre_edit = pending_pre_edit; - pending_pre_edit = NULL; - if (current_pre_edit) { - send_text_to_fltk(current_pre_edit, !bad_event, current_surface); - } else { - send_text_to_fltk(NULL, false, current_surface); - } - } - if (pending_commit) { - send_text_to_fltk(pending_commit, false, current_surface); - free(pending_commit); pending_commit = NULL; - } -} - - -static const struct zwp_text_input_v3_listener text_input_listener = { - .enter = text_input_enter, - .leave = text_input_leave, - .preedit_string = text_input_preedit_string, - .commit_string = text_input_commit_string, - .delete_surrounding_text = text_input_delete_surrounding_text, - .done = text_input_done, -}; - - -void Fl_Wayland_Screen_Driver::enable_im() { - if (text_input_base && !seat->text_input) { - seat->text_input = zwp_text_input_manager_v3_get_text_input(text_input_base, - seat->wl_seat); - //printf("seat->text_input=%p\n",seat->text_input); - zwp_text_input_v3_add_listener(seat->text_input, &text_input_listener, NULL); - } -} - - -void Fl_Wayland_Screen_Driver::disable_im() { - if (seat->text_input) { - zwp_text_input_v3_disable(seat->text_input); - zwp_text_input_v3_commit(seat->text_input); - zwp_text_input_v3_destroy(seat->text_input); - seat->text_input = NULL; - free(pending_pre_edit); pending_pre_edit = NULL; - free(current_pre_edit); current_pre_edit = NULL; - free(pending_commit); pending_commit = NULL; - } -} - - -static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) -{ - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct Fl_Wayland_Screen_Driver::seat *seat = - (struct Fl_Wayland_Screen_Driver::seat*)data; - if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) { - seat->wl_pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat); - seat->pointer_scale = 1; -#if HAVE_CURSOR_SHAPE - if (scr_driver->wp_cursor_shape_manager) { - scr_driver->wp_cursor_shape_device = - wp_cursor_shape_manager_v1_get_pointer(scr_driver->wp_cursor_shape_manager, seat->wl_pointer); - } -#endif // HAVE_CURSOR_SHAPE - init_cursors(seat); - } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) { - wl_pointer_release(seat->wl_pointer); - seat->wl_pointer = NULL; - } - - bool have_keyboard = seat->xkb_context && (capabilities & WL_SEAT_CAPABILITY_KEYBOARD); - if (have_keyboard && seat->wl_keyboard == NULL) { - seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(seat->wl_keyboard, - &wl_keyboard_listener, seat); -//fprintf(stderr, "wl_keyboard version=%d\n", wl_keyboard_get_version(seat->wl_keyboard)); - - } else if (!have_keyboard && seat->wl_keyboard != NULL) { - wl_keyboard_release(seat->wl_keyboard); - seat->wl_keyboard = NULL; - } - scr_driver->enable_im(); -} - - -static void seat_name(void *data, struct wl_seat *wl_seat, const char *name) { - struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data; - seat->name = strdup(name); -} - - -static struct wl_seat_listener seat_listener = { - seat_capabilities, - seat_name -}; - - -static void output_geometry(void *data, - struct wl_output *wl_output, - int32_t x, - int32_t y, - int32_t physical_width, - int32_t physical_height, - int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ - //fprintf(stderr, "output_geometry: x=%d y=%d physical=%dx%d\n",x,y,physical_width,physical_height); - Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data; - output->x = int(x); - output->y = int(y); - output->dpi = 96; // to elaborate -} - - -static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) -{ - Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data; - output->pixel_width = int(width); - output->pixel_height = int(height); - output->width = output->pixel_width; // until further notice - output->height = output->pixel_height; -//fprintf(stderr, "output_mode: [%p]=%dx%d\n",output->wl_output,width,height); -} - - -static void output_done(void *data, struct wl_output *wl_output) -{ - // Runs at startup and when desktop scale factor is changed or screen added - Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data; -//fprintf(stderr, "output_done output=%p\n",output); - Fl_X *xp = Fl_X::first; - while (xp) { // all mapped windows - struct wld_window *win = (struct wld_window*)xp->xid; - Fl_Window *W = win->fl_win; - if (win->buffer || W->as_gl_window()) { - if (W->as_gl_window()) { - wl_surface_set_buffer_scale(win->wl_surface, output->wld_scale); - Fl_Window_Driver::driver(W)->is_a_rescale(true); - W->resize(W->x(), W->y(), W->w(), W->h()); - Fl_Window_Driver::driver(W)->is_a_rescale(false); - } else { - Fl_Wayland_Graphics_Driver::buffer_release(win); - } - W->redraw(); - Fl_Window_Driver::driver(W)->flush(); - } - xp = xp->next; - } - output->done = true; - - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->screen_count_get() > 0) { // true when output_done runs after initial screen dectection - scr_driver->screen_count_set( wl_list_length(&(scr_driver->outputs)) ); - scr_driver->init_workarea(); - } -} - - -static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) { - Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data; - output->wld_scale = factor; -//fprintf(stderr,"output_scale: wl_output=%p factor=%d\n",wl_output, factor); - // rescale cursors of windows that map here and have a custom cursor - Fl_Window *win = Fl::first_window(); - while (win) { - struct wld_window *xid = fl_wl_xid(win); - struct Fl_Wayland_Window_Driver::surface_output *s_output; - // get 1st screen where window appears - s_output = wl_container_of(xid->outputs.next, s_output, link); - if (xid->custom_cursor && output == s_output->output) { - Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win); - driver->set_cursor_4args(xid->custom_cursor->rgb, - xid->custom_cursor->hotx, xid->custom_cursor->hoty, false); - }; - win = Fl::next_window(win); - } -} - - -static struct wl_output_listener output_listener = { - output_geometry, - output_mode, - output_done, - output_scale -}; - - -struct pair_bool { - bool found_gtk_shell; - bool found_wf_shell; -}; - - -// Notice: adding use of unstable protocol "XDG output" would allow FLTK to be notified -// in real time of changes to the relative location of multiple displays; -// with the present code, that information is received at startup only. -static void registry_handle_global(void *user_data, struct wl_registry *wl_registry, - uint32_t id, const char *interface, uint32_t version) { -//fprintf(stderr, "interface=%s version=%u\n", interface, version); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (strcmp(interface, "wl_compositor") == 0) { - if (version < 4) { - Fl::fatal("wl_compositor version >= 4 required"); - } - scr_driver->wl_compositor = (struct wl_compositor*)wl_registry_bind(wl_registry, - id, &wl_compositor_interface, 4); - - } else if (strcmp(interface, "wl_subcompositor") == 0) { - scr_driver->wl_subcompositor = (struct wl_subcompositor*)wl_registry_bind(wl_registry, - id, &wl_subcompositor_interface, 1); - - } else if (strcmp(interface, "wl_shm") == 0) { - scr_driver->wl_shm = (struct wl_shm*)wl_registry_bind(wl_registry, - id, &wl_shm_interface, 1); - - } else if (strcmp(interface, "wl_seat") == 0) { - if (version < 3) { - Fl::fatal("%s version 3 required but only version %i is available\n", - interface, version); - } - if (!scr_driver->seat) scr_driver->seat = - (struct Fl_Wayland_Screen_Driver::seat*)calloc(1, - sizeof(struct Fl_Wayland_Screen_Driver::seat)); -//fprintf(stderr, "registry_handle_global: seat=%p\n", scr_driver->seat); - wl_list_init(&scr_driver->seat->pointer_outputs); - scr_driver->seat->wl_seat = (wl_seat*)wl_registry_bind(wl_registry, id, - &wl_seat_interface, 3); - scr_driver->seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (scr_driver->seat->xkb_context) { - const char *locale = getenv("LC_ALL"); - if (!locale || !*locale) - locale = getenv("LC_CTYPE"); - if (!locale || !*locale) - locale = getenv("LANG"); - if (!locale || !*locale) - locale = "C"; - struct xkb_compose_table *table = - xkb_compose_table_new_from_locale(scr_driver->seat->xkb_context, locale, - XKB_COMPOSE_COMPILE_NO_FLAGS); - if (table) { - scr_driver->seat->xkb_compose_state = - xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS); - } - } - wl_seat_add_listener(scr_driver->seat->wl_seat, &seat_listener, scr_driver->seat); - if (scr_driver->seat->data_device_manager) { - scr_driver->seat->data_device = - wl_data_device_manager_get_data_device(scr_driver->seat->data_device_manager, - scr_driver->seat->wl_seat); - wl_data_device_add_listener(scr_driver->seat->data_device, - Fl_Wayland_Screen_Driver::p_data_device_listener, NULL); - } - - } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { - if (!scr_driver->seat) scr_driver->seat = - (struct Fl_Wayland_Screen_Driver::seat*)calloc(1, - sizeof(struct Fl_Wayland_Screen_Driver::seat)); - scr_driver->seat->data_device_manager = - (struct wl_data_device_manager*)wl_registry_bind(wl_registry, id, - &wl_data_device_manager_interface, - fl_min(version, 3)); - if (scr_driver->seat->wl_seat) { - scr_driver->seat->data_device = - wl_data_device_manager_get_data_device(scr_driver->seat->data_device_manager, - scr_driver->seat->wl_seat); - wl_data_device_add_listener(scr_driver->seat->data_device, - Fl_Wayland_Screen_Driver::p_data_device_listener, NULL); - } -//fprintf(stderr, "registry_handle_global: %s\n", interface); - - } else if (strcmp(interface, "wl_output") == 0) { - if (version < 2) { - Fl::fatal("%s version 2 required but only version %i is available\n", - interface, version); - } - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)calloc(1, sizeof *output); - output->id = id; - output->wld_scale = 1; -#ifdef WL_OUTPUT_RELEASE_SINCE_VERSION - const int used_version = WL_OUTPUT_RELEASE_SINCE_VERSION; -#else - const int used_version = 2; -#endif - output->wl_output = (struct wl_output*)wl_registry_bind(wl_registry, - id, &wl_output_interface, fl_min(used_version, version)); - output->gui_scale = 1.f; - wl_proxy_set_tag((struct wl_proxy *) output->wl_output, &proxy_tag); - wl_output_add_listener(output->wl_output, &output_listener, output); - // Put new screen in list of screens, but make sure it's not in list already - // which may occur after having removed a screen. - bool found = false; - Fl_Wayland_Screen_Driver::output *elt; - wl_list_for_each(elt, &scr_driver->outputs, link) { - if (elt == output) found = true; - } - if (!found) { // add to end of the linked list of displays - struct wl_list *e = &scr_driver->outputs; - while (e->next != &scr_driver->outputs) e = e->next; // move e to end of linked list - wl_list_insert(e, &output->link); - } -//fprintf(stderr, "wl_output: id=%d wl_output=%p \n", id, output->wl_output); - - } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { -//fprintf(stderr, "registry_handle_global interface=%s\n", interface); - scr_driver->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, id, - &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(scr_driver->xdg_wm_base, &xdg_wm_base_listener, NULL); - } else if (strstr(interface, "wf_shell_manager")) { - ((pair_bool*)user_data)->found_wf_shell = true; - } else if (strcmp(interface, "gtk_shell1") == 0) { - ((pair_bool*)user_data)->found_gtk_shell = true; - //fprintf(stderr, "Running the Mutter compositor\n"); - scr_driver->seat->gtk_shell = (struct gtk_shell1*)wl_registry_bind(wl_registry, id, - >k_shell1_interface, version); - } else if (strcmp(interface, "weston_desktop_shell") == 0) { - Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::WESTON; - //fprintf(stderr, "Running the Weston compositor\n"); - } else if (strcmp(interface, "org_kde_plasma_shell") == 0) { - Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::KWIN; - //fprintf(stderr, "Running the KWin compositor\n"); - } else if (strncmp(interface, "zowl_mach_ipc", 13) == 0) { - Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::OWL; - //fprintf(stderr, "Running the Owl compositor\n"); - if (wl_list_length(&scr_driver->outputs) == 0) { - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)calloc(1, sizeof *output); - output->id = 1; - output->wld_scale = 1; - output->gui_scale = 1.f; - output->width = 1440; output->height = 900; - output->pixel_width = 1440; output->pixel_height = 900; - output->done = true; - wl_list_insert(&(scr_driver->outputs), &output->link); - scr_driver->screen_count_set(1); - } - } else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) { - scr_driver->text_input_base = (struct zwp_text_input_manager_v3 *) - wl_registry_bind(wl_registry, id, &zwp_text_input_manager_v3_interface, 1); -//printf("scr_driver->text_input_base=%p version=%d\n",scr_driver->text_input_base,version); -#if HAVE_XDG_DIALOG - } else if (strcmp(interface, xdg_wm_dialog_v1_interface.name) == 0) { - scr_driver->xdg_wm_dialog = (struct xdg_wm_dialog_v1 *) - wl_registry_bind(wl_registry, id, &xdg_wm_dialog_v1_interface, 1); -#endif // HAVE_XDG_DIALOG -#if HAVE_CURSOR_SHAPE - } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { - scr_driver->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *) - wl_registry_bind(wl_registry, id, &wp_cursor_shape_manager_v1_interface, 1); -#endif // HAVE_CURSOR_SHAPE - } -} - - -static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - Fl_Wayland_Screen_Driver::output *output; -//fprintf(stderr, "registry_handle_global_remove data=%p id=%u\n", data, name); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - bool has_removed_screen = false; - wl_list_for_each(output, &(scr_driver->outputs), link) { // all screens - if (output->id == name) { // the screen being removed - wl_list_remove(&output->link); - wl_output_destroy(output->wl_output); - free(output); - has_removed_screen = true; - break; - } - } - if (has_removed_screen) { - scr_driver->screen_count_set( wl_list_length(&(scr_driver->outputs)) ); - scr_driver->init_workarea(); - } -} - - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - - -static void wayland_socket_callback(int fd, struct wl_display *display) -{ - if (wl_display_prepare_read(display) == -1) { - wl_display_dispatch_pending(display); - return; - } - wl_display_flush(display); - struct pollfd fds = (struct pollfd) { fd, POLLIN, 0 }; - if (poll(&fds, 1, 0) <= 0) { - wl_display_cancel_read(display); - return; - } - if (fds.revents & (POLLERR | POLLHUP)) { - wl_display_cancel_read(display); - goto fatal; - } - if (wl_display_read_events(display) == -1) - goto fatal; - if (wl_display_dispatch_pending(display) == -1) - goto fatal; - return; -fatal: - if (wl_display_get_error(display) == EPROTO) { - const struct wl_interface *interface; - int code = wl_display_get_protocol_error(display, &interface, NULL); - Fl::fatal("Fatal error %d in Wayland protocol: %s", - code, (interface ? interface->name : "unknown") ); - } else { - Fl::fatal("Fatal error while communicating with Wayland server: %s", - strerror(errno)); - } -} - - -Fl_Wayland_Screen_Driver::Fl_Wayland_Screen_Driver() : Fl_Unix_Screen_Driver() { - libdecor_context = NULL; - seat = NULL; - text_input_base = NULL; - reset_cursor(); - wl_registry = NULL; -#if HAVE_XDG_DIALOG - xdg_wm_dialog = NULL; -#endif -#if HAVE_CURSOR_SHAPE - wp_cursor_shape_manager = NULL; - wp_cursor_shape_device = NULL; -#endif -} - - -static void sync_done(void *data, struct wl_callback *cb, uint32_t time) { - // runs after all calls to registry_handle_global() - *(struct wl_callback **)data = NULL; - wl_callback_destroy(cb); - // keep processing until output_done() has run for each screen - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &scr_driver->outputs, link) { // each screen of the system - while (!output->done) wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - } - // Now all screens have been initialized - scr_driver->screen_count_set( wl_list_length(&(scr_driver->outputs)) ); - struct pair_bool *pair = (struct pair_bool*)wl_registry_get_user_data(scr_driver->wl_registry); - if (pair->found_gtk_shell || pair->found_wf_shell) { - Fl_Wayland_Screen_Driver::compositor = (pair->found_wf_shell ? - Fl_Wayland_Screen_Driver::WAYFIRE : Fl_Wayland_Screen_Driver::MUTTER); - } - if (scr_driver->seat) { -#if HAVE_CURSOR_SHAPE - if (!scr_driver->wp_cursor_shape_manager) -#endif - try_update_cursor(scr_driver->seat); - } - if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::OWL) scr_driver->init_workarea(); -} - - -static const struct wl_callback_listener sync_listener = { - sync_done -}; - - -static void do_atexit() { - if (Fl_Wayland_Screen_Driver::wl_display) { - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - } -} - - -void Fl_Wayland_Screen_Driver::open_display_platform() { - static bool beenHereDoneThat = false; - if (beenHereDoneThat) - return; - - beenHereDoneThat = true; - - if (!wl_display) { - wl_display = wl_display_connect(NULL); - if (!wl_display) { - Fl::fatal("No Wayland connection\n"); - } - } - //puts("Using Wayland backend"); - wl_list_init(&outputs); - - wl_registry = wl_display_get_registry(wl_display); - struct pair_bool pair = {false, false}; - wl_registry_add_listener(wl_registry, ®istry_listener, &pair); - struct wl_callback *registry_cb = wl_display_sync(wl_display); - wl_callback_add_listener(registry_cb, &sync_listener, ®istry_cb); - while (registry_cb) wl_display_dispatch(wl_display); - Fl::add_fd(wl_display_get_fd(wl_display), FL_READ, (Fl_FD_Handler)wayland_socket_callback, - wl_display); - fl_create_print_window(); - /* This is useful to avoid crash of the Wayland compositor after - FLTK apps terminate in certain situations: - - gnome-shell version < 44 (e.g. version 42.9) - - focus set to "follow-mouse" - See issue #821 for details. - */ - atexit(do_atexit); -} - - -void Fl_Wayland_Screen_Driver::close_display() { - if (!Fl_Wayland_Screen_Driver::wl_display) return; - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - if (text_input_base) { - disable_im(); - zwp_text_input_manager_v3_destroy(text_input_base); - text_input_base = NULL; - } - while (wl_list_length(&outputs) > 0) { - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - wl_list_remove(&output->link); - screen_count_set( wl_list_length(&outputs) ); - if (output->wl_output) { -#ifdef WL_OUTPUT_RELEASE_SINCE_VERSION - if (wl_output_get_version(output->wl_output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) - wl_output_release(output->wl_output); - else -#endif - wl_output_destroy(output->wl_output); - } - free(output); - break; - } - } - wl_subcompositor_destroy(wl_subcompositor); wl_subcompositor = NULL; - wl_surface_destroy(seat->cursor_surface); seat->cursor_surface = NULL; - if (seat->cursor_theme) { - wl_cursor_theme_destroy(seat->cursor_theme); - seat->cursor_theme = NULL; - } - wl_compositor_destroy(wl_compositor); wl_compositor = NULL; - // wl_shm-related data - if (Fl_Wayland_Graphics_Driver::current_pool) { - struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data *pool_data = - (struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data*) - wl_shm_pool_get_user_data(Fl_Wayland_Graphics_Driver::current_pool); - wl_shm_pool_destroy(Fl_Wayland_Graphics_Driver::current_pool); - Fl_Wayland_Graphics_Driver::current_pool = NULL; - /*int err = */munmap(pool_data->pool_memory, pool_data->pool_size); - //printf("close_display munmap(%p)->%d\n", pool_data->pool_memory, err); - free(pool_data); - } - wl_shm_destroy(wl_shm); wl_shm = NULL; - if (seat->wl_keyboard) { - if (seat->xkb_state) { - xkb_state_unref(seat->xkb_state); - seat->xkb_state = NULL; - } - if (seat->xkb_keymap) { - xkb_keymap_unref(seat->xkb_keymap); - seat->xkb_keymap = NULL; - } - wl_keyboard_destroy(seat->wl_keyboard); - seat->wl_keyboard = NULL; - } - wl_pointer_destroy(seat->wl_pointer); seat->wl_pointer = NULL; - if (seat->xkb_compose_state) { - xkb_compose_state_unref(seat->xkb_compose_state); - seat->xkb_compose_state = NULL; - } - if (seat->xkb_context) { - xkb_context_unref(seat->xkb_context); - seat->xkb_context = NULL; - } - if (seat->data_source) { - wl_data_source_destroy(seat->data_source); - seat->data_source = NULL; - } - wl_data_device_destroy(seat->data_device); seat->data_device = NULL; - wl_data_device_manager_destroy(seat->data_device_manager); - seat->data_device_manager = NULL; - wl_seat_destroy(seat->wl_seat); seat->wl_seat = NULL; - if (seat->name) free(seat->name); - free(seat); seat = NULL; - if (libdecor_context) { - libdecor_unref(libdecor_context); - libdecor_context = NULL; - } - xdg_wm_base_destroy(xdg_wm_base); xdg_wm_base = NULL; - Fl_Wayland_Plugin *plugin = Fl_Wayland_Window_Driver::gl_plugin(); - if (plugin) plugin->terminate(); -#if HAVE_XDG_DIALOG - if (xdg_wm_dialog) { - xdg_wm_dialog_v1_destroy(xdg_wm_dialog); - xdg_wm_dialog = NULL; - } -#endif // HAVE_XDG_DIALOG -#if HAVE_CURSOR_SHAPE - if (wp_cursor_shape_device ) { - wp_cursor_shape_device_v1_destroy(wp_cursor_shape_device); - wp_cursor_shape_device = NULL; - } - if (wp_cursor_shape_manager ) { - wp_cursor_shape_manager_v1_destroy(wp_cursor_shape_manager); - wp_cursor_shape_manager = NULL; - } -#endif // HAVE_CURSOR_SHAPE - - Fl::remove_fd(wl_display_get_fd(Fl_Wayland_Screen_Driver::wl_display)); - wl_registry_destroy(wl_registry); wl_registry = NULL; - wl_display_disconnect(Fl_Wayland_Screen_Driver::wl_display); - Fl_Wayland_Screen_Driver::wl_display = NULL; - delete Fl_Display_Device::display_device()->driver(); - delete Fl_Display_Device::display_device(); - delete Fl::system_driver(); - delete this; -} - - -struct configure_s { int W, H; uint32_t state; }; - -static void xdg_toplevel_configure(void *v, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, struct wl_array *states) -{ - struct configure_s *data = (struct configure_s*)v; - data->W = width; - data->H = height; - data->state = (width && height && states ? *(uint32_t *)(states->data) : 0); -} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure, -}; - - -static bool compute_full_and_maximized_areas(Fl_Wayland_Screen_Driver::output *output, - int& Wfullscreen, int& Hfullscreen, - int& Wworkarea, int& Hworkarea) { - if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::unspecified) { - Wfullscreen = 0; - return false; - } - bool found_workarea = false; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct wl_surface *wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor); - wl_surface_set_opaque_region(wl_surface, NULL); - struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, wl_surface); - struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); - struct configure_s data = {0, 0, 0}; - xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, &data); - xdg_toplevel_set_fullscreen(xdg_toplevel, output->wl_output); - wl_surface_commit(wl_surface); // necessary under KWin - while (data.state != XDG_TOPLEVEL_STATE_FULLSCREEN) - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - Wfullscreen = data.W; - Hfullscreen = data.H; - if (Wfullscreen && Hfullscreen && wl_list_length(&scr_driver->outputs) == 1) { - struct wl_surface *wl_surface2 = wl_compositor_create_surface(scr_driver->wl_compositor); - struct xdg_surface *xdg_surface2 = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, wl_surface2); - struct xdg_toplevel *xdg_toplevel2 = xdg_surface_get_toplevel(xdg_surface2); - struct configure_s data2 = {0, 0, 0}; - xdg_toplevel_add_listener(xdg_toplevel2, &xdg_toplevel_listener, &data2); - xdg_toplevel_set_parent(xdg_toplevel2, xdg_toplevel); - xdg_toplevel_set_maximized(xdg_toplevel2); - wl_surface_commit(wl_surface2); // necessary under KWin - while (data2.state != XDG_TOPLEVEL_STATE_MAXIMIZED) - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - Wworkarea = data2.W; - Hworkarea = data2.H; - xdg_toplevel_destroy(xdg_toplevel2); - xdg_surface_destroy(xdg_surface2); - wl_surface_destroy(wl_surface2); - if (Wworkarea == Wfullscreen && Hworkarea < Hfullscreen && Hworkarea > Hfullscreen - 80) - found_workarea = true; - if (Hworkarea == Hfullscreen && Wworkarea < Wfullscreen && Wworkarea > Wfullscreen - 80) - found_workarea = true; - } else { - Wworkarea = Wfullscreen; - Hworkarea = Hfullscreen; - } - xdg_toplevel_destroy(xdg_toplevel); - xdg_surface_destroy(xdg_surface); - wl_surface_destroy(wl_surface); - /*int fractional_scale = int(100 * (output->pixel_width / float(Wfullscreen))); - printf("fullscreen=%dx%d workarea=%dx%d fractional_scale=%d%% wld_s=%d\n", - Wfullscreen,Hfullscreen,Wworkarea,Hworkarea,fractional_scale,output->wld_scale);*/ - return found_workarea; -} - -static int workarea_xywh[4] = { -1, -1, -1, -1 }; - - -/* Implementation note about computing work area and about handling fractional scaling. - - FLTK computes 2 pairs of (WxH) values for each display: - 1) (pixel_width x pixel_height) gives the size in pixel of a display. It's unchanged by - any scaling applied by the compositor; it's assigned by function output_mode(). - 2) (width x height) gives the size in pixels of a buffer that would fully cover the display. - When the active scaling is non-fractional, these equations hold: - pixel_width = width = wld_scale * configured-width-of-fullscreen-window - pixel_height = height = wld_scale * configured-height-of-fullscreen-window - - When fractional scaling is active, buffers received from client are scaled down - by the compositor and mapped to screen. These equations hold: - pixel_width < width = wld_scale * configured-width-of-fullscreen-window - pixel_height < height = wld_scale * configured-height-of-fullscreen-window - - One way for a client to discover that fractional scaling is active on a given display - is to ask for a fullscreen window on that display, get its configured size and compare - it to that display's pixel size. That's what function compute_full_and_maximized_areas() does. - - One way for a client to discover the work area size of a display is to get the configured size - of a maximized window on that display. FLTK didn't find a way to control in general - on what display the compositor puts a maximized window. Therefore, FLTK computes an exact - work area size only when the system contains a single display. We create first a fullscreen - window on the display and then we create a maximized window made a child of the - fullscreen one and record its configured size. That's also done by function - compute_full_and_maximized_areas(). - */ - -void Fl_Wayland_Screen_Driver::init_workarea() -{ - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); // important after screen removal - bool need_init_workarea = true; - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - int Wfullscreen, Hfullscreen, Wworkarea, Hworkarea; - bool found_workarea = compute_full_and_maximized_areas(output, Wfullscreen, Hfullscreen, Wworkarea, Hworkarea); - if (Wfullscreen && Hfullscreen) { // skip sway which puts 0 there - output->width = Wfullscreen * output->wld_scale; // pixels - output->height = Hfullscreen * output->wld_scale; // pixels - if (found_workarea) { - workarea_xywh[0] = output->x; // pixels - workarea_xywh[1] = output->y; // pixels - workarea_xywh[2] = Wworkarea * output->wld_scale; // pixels - workarea_xywh[3] = Hworkarea * output->wld_scale; // pixels - need_init_workarea = false; - } - } - } - if (need_init_workarea) { - screen_xywh(workarea_xywh[0], workarea_xywh[1], workarea_xywh[2], workarea_xywh[3], 0); - } - Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); -} - - -int Fl_Wayland_Screen_Driver::x() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - break; - } - return workarea_xywh[0] / (output->gui_scale * output->wld_scale); -} - - -int Fl_Wayland_Screen_Driver::y() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - break; - } - return workarea_xywh[1] / (output->gui_scale * output->wld_scale); -} - - -int Fl_Wayland_Screen_Driver::w() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - break; - } - return workarea_xywh[2] / (output->gui_scale * output->wld_scale); -} - - -int Fl_Wayland_Screen_Driver::h() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &outputs, link) { - break; - } - return workarea_xywh[3] / (output->gui_scale * output->wld_scale); -} - - -void Fl_Wayland_Screen_Driver::init() { - if (!Fl_Wayland_Screen_Driver::wl_registry) open_display(); -} - - -void Fl_Wayland_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - if (n < 0 || n >= num_screens) n = 0; - if (n == 0) { // for the main screen, these return the work area - X = Fl::x(); - Y = Fl::y(); - W = Fl::w(); - H = Fl::h(); - } else { // for other screens, work area is full screen, - screen_xywh(X, Y, W, H, n); - } -} - - -void Fl_Wayland_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - - if ((n < 0) || (n >= num_screens)) - n = 0; - - if (num_screens > 0) { - Fl_Wayland_Screen_Driver::output *output; - int i = 0; - wl_list_for_each(output, &outputs, link) { - if (i++ == n) { // n'th screen of the system - float s = output->gui_scale * output->wld_scale; - X = output->x / s; - Y = output->y / s; - W = output->width / s; - H = output->height / s; - break; - } - } - } -} - - -void Fl_Wayland_Screen_Driver::screen_dpi(float &h, float &v, int n) -{ - if (num_screens < 0) init(); - h = v = 0.0f; - - if (n >= 0 && n < num_screens) { - Fl_Wayland_Screen_Driver::output *output; - int i = 0; - wl_list_for_each(output, &outputs, link) { - if (i++ == n) { // n'th screen of the system - h = output->dpi; - v = output->dpi; - break; - } - } - } -} - - -// Implements fl_beep(). See documentation in src/fl_ask.cxx. -void Fl_Wayland_Screen_Driver::beep(int type) -{ - fprintf(stderr, "\007"); -} - - -void Fl_Wayland_Screen_Driver::flush() -{ - if (Fl_Wayland_Screen_Driver::wl_display) { - wl_display_flush(Fl_Wayland_Screen_Driver::wl_display); - } -} - - -extern void fl_fix_focus(); // in Fl.cxx - - -void Fl_Wayland_Screen_Driver::grab(Fl_Window* win) -{ - if (win) { - if (!Fl::grab()) { - } - Fl::grab_ = win; // FIXME: Fl::grab_ "should be private", but we need - // a way to *set* the variable from the driver! - } else { - if (Fl::grab()) { - // We must keep the grab in the non-EWMH fullscreen case - Fl::grab_ = 0; // FIXME: Fl::grab_ "should be private", but we need - // a way to *set* the variable from the driver! - fl_fix_focus(); - } - } -} - - -static void set_selection_color(uchar r, uchar g, uchar b) -{ - Fl::set_color(FL_SELECTION_COLOR,r,g,b); -} - - -static void getsyscolor(const char *key1, const char* key2, const char *arg, - const char *defarg, void (*func)(uchar,uchar,uchar)) { - uchar r, g, b; - if (!arg) arg = defarg; - if (!Fl::screen_driver()->parse_color(arg, r, g, b)) - Fl::error("Unknown color: %s", arg); - else - func(r, g, b); -} - - -void Fl_Wayland_Screen_Driver::get_system_colors() -{ - open_display(); - const char* key1 = 0; - if (Fl::first_window()) key1 = Fl::first_window()->xclass(); - if (!key1) key1 = "fltk"; - if (!bg2_set) - getsyscolor("Text","background", fl_bg2, "#ffffff", Fl::background2); - if (!fg_set) - getsyscolor(key1, "foreground", fl_fg, "#000000", Fl::foreground); - if (!bg_set) - getsyscolor(key1, "background", fl_bg, "#c0c0c0", Fl::background); - getsyscolor("Text", "selectBackground", 0, "#000080", set_selection_color); -} - - -Fl_RGB_Image *Fl_Wayland_Screen_Driver::read_win_rectangle(int X, int Y, int w, int h, - Fl_Window *win, - bool ignore, bool *p_ignore) { - struct wld_window* xid = win ? fl_wl_xid(win) : NULL; - if (win && (!xid || !xid->buffer)) return NULL; - struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer; - if (win) buffer = &xid->buffer->draw_buffer; - else { - Fl_Image_Surface_Driver *dr = (Fl_Image_Surface_Driver*)Fl_Surface_Device::surface(); - buffer = Fl_Wayland_Graphics_Driver::offscreen_buffer( - dr->image_surface()->offscreen()); - } - float s = win ? - Fl_Wayland_Window_Driver::driver(win)->wld_scale() * scale(win->screen_num()) : - Fl_Surface_Device::surface()->driver()->scale(); - int Xs, Ys, ws, hs; - if (s == 1) { - Xs = X; Ys = Y; ws = w; hs = h; - } else { - Xs = Fl_Scalable_Graphics_Driver::floor(X, s); - Ys = Fl_Scalable_Graphics_Driver::floor(Y, s); - ws = Fl_Scalable_Graphics_Driver::floor(X+w, s) - Xs; - hs = Fl_Scalable_Graphics_Driver::floor(Y+h, s) - Ys; - } - if (ws == 0 || hs == 0) return NULL; - uchar *data = new uchar[ws * hs * 3]; - uchar *p = data, *q; - for (int j = 0; j < hs; j++) { - q = buffer->buffer + (j+Ys) * buffer->stride + 4 * Xs; - for (int i = 0; i < ws; i++) { - *p++ = *(q+2); // R - *p++ = *(q+1); // G - *p++ = *q; // B - q += 4; - } - } - Fl_RGB_Image *rgb = new Fl_RGB_Image(data, ws, hs, 3); - rgb->alloc_array = 1; - return rgb; -} - - -void Fl_Wayland_Screen_Driver::offscreen_size(Fl_Offscreen off_, int &width, int &height) -{ - struct Fl_Wayland_Graphics_Driver::draw_buffer *off = Fl_Wayland_Graphics_Driver::offscreen_buffer(off_); - width = off->width; - height = off->data_size / off->stride; -} - - -float Fl_Wayland_Screen_Driver::scale(int n) { - Fl_Wayland_Screen_Driver::output *output; - int i = 0; - wl_list_for_each(output, &outputs, link) { - if (i++ == n) break; - } - return output->gui_scale; -} - - -void Fl_Wayland_Screen_Driver::scale(int n, float f) { - Fl_Wayland_Screen_Driver::output *output; - int i = 0; - wl_list_for_each(output, &outputs, link) { - if (i++ == n) { - output->gui_scale = f; - return; - } - } -} - - -void Fl_Wayland_Screen_Driver::set_cursor() { - do_set_cursor(seat); -} - - -struct wl_cursor *Fl_Wayland_Screen_Driver::default_cursor() { - return seat->default_cursor; -} - - -void Fl_Wayland_Screen_Driver::default_cursor(struct wl_cursor *cursor) { - seat->default_cursor = cursor; - do_set_cursor(seat); -} - - -struct wl_cursor *Fl_Wayland_Screen_Driver::cache_cursor(const char *cursor_name) { - return wl_cursor_theme_get_cursor(seat->cursor_theme, cursor_name); -} - - -void Fl_Wayland_Screen_Driver::reset_cursor() { - for (int i = 0; i < cursor_count; i++) xc_cursor[i] = NULL; -} - - -uint32_t Fl_Wayland_Screen_Driver::get_serial() { - return seat->serial; -} - - -struct wl_seat*Fl_Wayland_Screen_Driver::get_wl_seat() { - return seat->wl_seat; -} - - -char *Fl_Wayland_Screen_Driver::get_seat_name() { - return seat->name; -} - - -struct xkb_keymap *Fl_Wayland_Screen_Driver::get_xkb_keymap() { - return seat->xkb_keymap; -} - - -int Fl_Wayland_Screen_Driver::get_mouse(int &xx, int &yy) { - open_display(); - xx = Fl::e_x_root; yy = Fl::e_y_root; - if (!seat->pointer_focus) return 0; - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus); - if (!win) return 0; - int snum = Fl_Window_Driver::driver(win)->screen_num(); -//printf("get_mouse(%dx%d)->%d\n", xx, yy, snum); - return snum; -} - - -void Fl_Wayland_Screen_Driver::set_spot(int font, int height, int x, int y, int w, int h, Fl_Window *win) { - Fl_Wayland_Screen_Driver::insertion_point_location(x, y, height); -} - - -void Fl_Wayland_Screen_Driver::reset_spot() { - Fl::compose_state = 0; - Fl_Wayland_Screen_Driver::next_marked_length = 0; - Fl_Wayland_Screen_Driver::insertion_point_location_is_valid = false; -} - - -void Fl_Wayland_Screen_Driver::display(const char *d) -{ - if (d && !wl_registry) { // if display was opened, it's too late - if (wl_display) { - // only the wl_display_connect() call was done, redo it because the target - // Wayland compositor may be different - wl_display_disconnect(wl_display); - } - wl_display = wl_display_connect(d); - if (!wl_display) { - fprintf(stderr, "Error: '%s' is not an active Wayland socket\n", d); - exit(1); - } - } -} - - -void *Fl_Wayland_Screen_Driver::control_maximize_button(void *data) { - // The code below aims at removing the calling window's fullscreen button - // while dialog runs. Unfortunately, it doesn't work with some X11 window managers - // (e.g., KDE, xfce) because the button goes away but doesn't come back, - // so we move this code to a virtual member function. - // Noticeably, this code works OK under Wayland. - struct win_dims { - Fl_Widget_Tracker *tracker; - int minw, minh, maxw, maxh; - struct win_dims *next; - }; - - if (!data) { // this call turns each decorated window's maximize button off - struct win_dims *first_dim = NULL; - // consider all bordered, top-level FLTK windows - Fl_Window *win = Fl::first_window(); - while (win) { - if (!win->parent() && win->border() && - !( ((struct wld_window*)Fl_X::flx(win)->xid)->state & - LIBDECOR_WINDOW_STATE_MAXIMIZED) ) { - win_dims *dim = new win_dims; - dim->tracker = new Fl_Widget_Tracker(win); - win->get_size_range(&dim->minw, &dim->minh, &dim->maxw, &dim->maxh, NULL, NULL, NULL); - //make win un-resizable - win->size_range(win->w(), win->h(), win->w(), win->h()); - dim->next = first_dim; - first_dim = dim; - } - win = Fl::next_window(win); - } - return first_dim; - } else { // this call returns each decorated window's maximize button to its previous state - win_dims *first_dim = (win_dims *)data; - while (first_dim) { - win_dims *dim = first_dim; - //give back win its resizing parameters - if (dim->tracker->exists()) { - Fl_Window *win = (Fl_Window*)dim->tracker->widget(); - win->size_range(dim->minw, dim->minh, dim->maxw, dim->maxh); - } - first_dim = dim->next; - delete dim->tracker; - delete dim; - } - return NULL; - } -} - - -int Fl_Wayland_Screen_Driver::poll_or_select_with_delay(double time_to_wait) { - if (wl_display_dispatch_pending(wl_display) > 0) return 1; - return Fl_Unix_Screen_Driver::poll_or_select_with_delay(time_to_wait); -} - - -// like Fl_Wayland_Screen_Driver::poll_or_select_with_delay(0.0) except no callbacks are done: -int Fl_Wayland_Screen_Driver::poll_or_select() { - int ret = wl_display_prepare_read(wl_display); - if (ret == 0) wl_display_cancel_read(wl_display); - else return 1; - return Fl_Unix_Screen_Driver::poll_or_select(); -} - - -int Fl_Wayland_Screen_Driver::event_key(int k) { - if (k >= 'A' && k <= 'Z') k += 32; - return (search_int_vector(key_vector, k) >= 0); -} - - -int Fl_Wayland_Screen_Driver::get_key(int k) { - return event_key(k); -} - - -float Fl_Wayland_Screen_Driver::base_scale(int numscreen) { - const char *p; - float factor = 1; - if ((p = fl_getenv("FLTK_SCALING_FACTOR"))) { - sscanf(p, "%f", &factor); - } - return factor; -} - - -struct wl_display *fl_wl_display() { - return Fl_Wayland_Screen_Driver::wl_display; -} diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H deleted file mode 100644 index c5c1bee50..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H +++ /dev/null @@ -1,185 +0,0 @@ -// -// Definition of Wayland window driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_Wayland_Window_Driver.H - \brief Definition of Wayland window driver. - */ - -#ifndef FL_WAYLAND_WINDOW_DRIVER_H -#define FL_WAYLAND_WINDOW_DRIVER_H - -#include -#include "../../Fl_Window_Driver.H" -#include -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" - - -/* - Move everything here that manages the native window interface. - - There is one window driver for each Fl_Window. Window drivers manage window - actions such as resizing, events, decoration, fullscreen modes, etc. . All - drawing and rendering is managed by the Surface device and the associated - graphics driver. - - - window specific event handling - - window types and styles, depth, etc. - - decorations - */ - -typedef struct _cairo_pattern cairo_pattern_t; -typedef struct _cairo_rectangle_int cairo_rectangle_int_t; -class Fl_Wayland_Plugin; - - -class Fl_Wayland_Window_Driver : public Fl_Window_Driver -{ - friend class Fl_Wayland_Gl_Window_Driver; -private: - struct shape_data_type { - int lw_; ///< width of shape image - int lh_; ///< height of shape image - Fl_Image* shape_; ///< shape image - cairo_pattern_t *mask_pattern_; - } *shape_data_; - bool can_expand_outside_parent_; // specially to allow window docking (#987) - cairo_rectangle_int_t *subRect_; // makes sure subwindow remains inside its parent window - static bool in_flush_; // useful for progressive window drawing - Fl_Cursor standard_cursor_; // window's standard custom kind - struct gl_start_support *gl_start_support_; // for support of gl_start/gl_finish - bool is_popup_window_; -public: - inline Fl_Cursor standard_cursor() { return standard_cursor_; } - bool in_handle_configure; // distinguish OS and user window resize - - struct surface_output { // for linked list of displays where a surface maps - struct Fl_Wayland_Screen_Driver::output *output; - struct wl_list link; - }; - struct custom_cursor { - struct wl_cursor *wl_cursor; - const Fl_RGB_Image *rgb; - int hotx, hoty; - }; - static void delete_cursor(struct custom_cursor *custom, bool delete_rgb = true); - void decorated_win_size(int &w, int &h); - void shape_bitmap_(Fl_Image* b); - void shape_alpha_(Fl_Image* img, int offset) override; - FL_EXPORT int wld_scale(); // used by class Fl_Wayland_Gl_Window_Driver - cairo_rectangle_int_t *subRect() { return subRect_; } // getter - void subRect(cairo_rectangle_int_t *r); // setter - void checkSubwindowFrame(); - enum kind {DECORATED, SUBWINDOW, POPUP, UNFRAMED}; - struct xdg_toplevel *xdg_toplevel(); - Fl_Wayland_Window_Driver(Fl_Window*); - virtual ~Fl_Wayland_Window_Driver(); - static struct wld_window *wld_window; - static Fl_Window *surface_to_window(struct wl_surface *); - - static inline Fl_Wayland_Window_Driver* driver(const Fl_Window *w) { - return (Fl_Wayland_Window_Driver*)Fl_Window_Driver::driver(w); - } - static Fl_Wayland_Plugin *gl_plugin(); - - // --- window data - int decorated_w() override; - int decorated_h() override; - const Fl_Image* shape() override; - - // --- window management - void makeWindow() override; - void take_focus() override; - void flush() override; - void flush_overlay() override; - void draw_end() override; - void make_current() override; - void show() override; - void resize(int X,int Y,int W,int H) override; - void label(const char *name, const char *mininame) override; - void hide() override; - void map() override; - void unmap() override; - void fullscreen_on() override; - void fullscreen_off(int X, int Y, int W, int H) override; - void maximize() override; - void un_maximize() override; - void use_border() override; - void size_range() override; - void iconize() override; - void decoration_sizes(int *top, int *left, int *right, int *bottom) override; - // --- window cursor stuff - int set_cursor(Fl_Cursor) override; - int set_cursor(const Fl_RGB_Image*, int, int) override; - int set_cursor_4args(const Fl_RGB_Image*, int, int, bool); - - void shape(const Fl_Image* img) override; - void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, - Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) override; - int scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, - void (*draw_area)(void*, int,int,int,int), void* data) override; - void wait_for_expose() override; - // menu-related stuff - void reposition_menu_window(int x, int y) override; - void menu_window_area(int &X, int &Y, int &W, int &H, int nscreen = -1) override; - static bool new_popup; // to support tall menu buttons - bool process_menu_or_tooltip(struct wld_window *); - static Fl_Window *previous_floatingtitle; // to support floating menuwindow w/ title - void allow_expand_outside_parent() override { can_expand_outside_parent_ = true; } -}; - - -struct wld_window { - Fl_Window *fl_win; - struct wl_list outputs; // linked list of displays where part or whole of window maps - struct wl_surface *wl_surface; - struct wl_callback *frame_cb; - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; - struct xdg_surface *xdg_surface; - union { // for each value of kind - struct libdecor_frame *frame; - struct wl_subsurface *subsurface; - struct xdg_popup *xdg_popup; - struct xdg_toplevel *xdg_toplevel; - }; - // non-null when using custom cursor - struct Fl_Wayland_Window_Driver::custom_cursor *custom_cursor; -#if HAVE_XDG_DIALOG - struct xdg_dialog_v1 *xdg_dialog; -#endif - enum Fl_Wayland_Window_Driver::kind kind; - int configured_width; - int configured_height; - int floating_width; - int floating_height; - int state; - bool covered; // specially for Mutter and issue #878 -}; - - -class Fl_Wayland_Plugin : public Fl_Plugin { -public: - Fl_Wayland_Plugin(const char *pluginName) : Fl_Plugin(klass(), pluginName) { } - virtual const char *klass() { return "wayland.fltk.org"; } - virtual const char *name() = 0; - virtual void do_swap(Fl_Window*) = 0; - virtual void invalidate(Fl_Window*) = 0; - virtual void terminate() = 0; - virtual void destroy(struct gl_start_support *) = 0; -}; - -#endif // FL_WAYLAND_WINDOW_DRIVER_H diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx deleted file mode 100644 index 0495fb7bc..000000000 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ /dev/null @@ -1,2191 +0,0 @@ -// -// Implementation of the Wayland window driver. -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include -#include -#include "../../../libdecor/build/fl_libdecor.h" -#include "xdg-shell-client-protocol.h" -#include "gtk-shell-client-protocol.h" -#if HAVE_XDG_DIALOG -# include "xdg-dialog-client-protocol.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // for ceil() -#include // for pid_t -#include // for getpid() - -struct cursor_image { // as in wayland-cursor.c of the Wayland project source code - struct wl_cursor_image image; - struct wl_cursor_theme *theme; - struct wl_buffer *buffer; - int offset; /* data offset of this image in the shm pool */ -}; - -extern "C" { -# include "../../../libdecor/src/libdecor-plugin.h" - uchar *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame, int *w, int *h, int *stride); -} - -#define fl_max(a,b) ((a) > (b) ? (a) : (b)) -#define fl_min(a,b) ((a) < (b) ? (a) : (b)) - -#if !defined(FLTK_USE_X11) -Window fl_window = 0; -#endif - - -struct wld_window *Fl_Wayland_Window_Driver::wld_window = NULL; -bool Fl_Wayland_Window_Driver::new_popup = false; // to support tall menu buttons -// A menutitle to be mapped later as the child of a menuwindow -Fl_Window *Fl_Wayland_Window_Driver::previous_floatingtitle = NULL; - - -Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_Driver(win) -{ - shape_data_ = NULL; - standard_cursor_ = FL_CURSOR_DEFAULT; - in_handle_configure = false; - screen_num_ = -1; - gl_start_support_ = NULL; - subRect_ = NULL; - is_popup_window_ = false; - can_expand_outside_parent_ = false; -} - - -void Fl_Wayland_Window_Driver::delete_cursor( - struct Fl_Wayland_Window_Driver::custom_cursor *custom, bool delete_rgb) { - struct wl_cursor *wl_cursor = custom->wl_cursor; - struct cursor_image *new_image = (struct cursor_image*)wl_cursor->images[0]; - struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen = - (struct Fl_Wayland_Graphics_Driver::wld_buffer *) - wl_buffer_get_user_data(new_image->buffer); - struct wld_window fake_xid; - memset(&fake_xid, 0, sizeof(fake_xid)); - fake_xid.buffer = offscreen; - Fl_Wayland_Graphics_Driver::buffer_release(&fake_xid); - free(new_image); - free(wl_cursor->images); - free(wl_cursor->name); - free(wl_cursor); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->default_cursor() == wl_cursor) { - scr_driver->default_cursor(scr_driver->xc_cursor[Fl_Wayland_Screen_Driver::arrow]); - } - if (delete_rgb) delete custom->rgb; - delete custom; -} - - -Fl_Wayland_Window_Driver::~Fl_Wayland_Window_Driver() -{ - if (shape_data_) { - cairo_surface_t *surface; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface); - uchar *data = cairo_image_surface_get_data(surface); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] data; - delete shape_data_; - } - if (subRect_) delete subRect_; - if (gl_start_support_) { // occurs only if gl_start/gl_finish was used - gl_plugin()->destroy(gl_start_support_); - } -} - - -void Fl_Wayland_Window_Driver::decorated_win_size(int &w, int &h) -{ - Fl_Window *win = pWindow; - w = win->w(); - h = win->h(); - if (!win->shown() || win->parent() || !win->border() || !win->visible()) return; - int X, titlebar_height; - libdecor_frame_translate_coordinate(fl_wl_xid(win)->frame, 0, 0, &X, &titlebar_height); -//printf("titlebar_height=%d\n",titlebar_height); - h = win->h() + ceil(titlebar_height / Fl::screen_scale(win->screen_num())); -} - - -int Fl_Wayland_Window_Driver::decorated_h() -{ - int w, h; - decorated_win_size(w, h); - return h; -} - - -int Fl_Wayland_Window_Driver::decorated_w() -{ - int w, h; - decorated_win_size(w, h); - return w; -} - - -struct xdg_toplevel *Fl_Wayland_Window_Driver::xdg_toplevel() { - struct wld_window * w = fl_wl_xid(pWindow); - struct xdg_toplevel *top = NULL; - if (w->kind == DECORATED) top = libdecor_frame_get_xdg_toplevel(w->frame); - else if (w->kind == UNFRAMED) top = w->xdg_toplevel; - return top; -} - - -void Fl_Wayland_Window_Driver::take_focus() -{ - struct wld_window *w = fl_wl_xid(pWindow); - if (w) { - Fl_Window *old_first = Fl::first_window(); - struct wld_window *first_xid = (old_first ? fl_wl_xid(old_first->top_window()) : NULL); - if (first_xid && first_xid != w && xdg_toplevel()) { - // this will move the target window to the front - Fl_Wayland_Window_Driver *top_dr = - Fl_Wayland_Window_Driver::driver(old_first->top_window()); - xdg_toplevel_set_parent(xdg_toplevel(), top_dr->xdg_toplevel()); - // this will remove the parent-child relationship - xdg_toplevel_set_parent(xdg_toplevel(), NULL); - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - } - // this sets the first window - fl_wl_find(w); - } -} - - -void Fl_Wayland_Window_Driver::flush_overlay() -{ - if (!shown()) return; - Fl_Overlay_Window *oWindow = pWindow->as_overlay_window(); - int erase_overlay = (pWindow->damage()&FL_DAMAGE_OVERLAY) | (overlay() == oWindow); - pWindow->clear_damage((uchar)(pWindow->damage()&~FL_DAMAGE_OVERLAY)); - pWindow->make_current(); - if (!other_xid) { - other_xid = new Fl_Image_Surface(oWindow->w(), oWindow->h(), 1); - oWindow->clear_damage(FL_DAMAGE_ALL); - } - if (oWindow->damage() & ~FL_DAMAGE_EXPOSE) { - Fl_X *myi = Fl_X::flx(pWindow); - fl_clip_region(myi->region); myi->region = 0; - Fl_Surface_Device::push_current(other_xid); - draw(); - Fl_Surface_Device::pop_current(); - } - if (erase_overlay) fl_clip_region(0); - if (other_xid) { - struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer = - Fl_Wayland_Graphics_Driver::offscreen_buffer(other_xid->offscreen()); - struct wld_window *xid = fl_wl_xid(pWindow); - struct Fl_Wayland_Graphics_Driver::wld_buffer *wbuffer = xid->buffer; - if (wbuffer->draw_buffer.data_size != buffer->data_size) { - fl_copy_offscreen(0, 0, oWindow->w(), oWindow->h(), other_xid->offscreen(), 0, 0); - } else { - memcpy(wbuffer->draw_buffer.buffer, buffer->buffer, wbuffer->draw_buffer.data_size); - } - } - if (overlay() == oWindow) oWindow->draw_overlay(); -} - - -const Fl_Image* Fl_Wayland_Window_Driver::shape() { - return shape_data_ ? shape_data_->shape_ : NULL; -} - - -void Fl_Wayland_Window_Driver::shape_bitmap_(Fl_Image* b) { - shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::bitmap_to_pattern( - (Fl_Bitmap*)b, true, NULL); - shape_data_->shape_ = b; - shape_data_->lw_ = b->data_w(); - shape_data_->lh_ = b->data_h(); -} - - -void Fl_Wayland_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { - int i, j, d = img->d(), w = img->data_w(), h = img->data_h(); - int bytesperrow = cairo_format_stride_for_width(CAIRO_FORMAT_A1, w); - unsigned u; - uchar byte, onebit; - // build a CAIRO_FORMAT_A1 surface covering the non-fully transparent/black part of the image - uchar* bits = new uchar[h*bytesperrow]; // to store the surface data - const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of pixels - for (i = 0; i < h; i++) { - uchar *p = (uchar*)bits + i * bytesperrow; - byte = 0; - onebit = 1; - for (j = 0; j < w; j++) { - if (d == 3) { - u = *alpha; - u += *(alpha+1); - u += *(alpha+2); - } - else u = *alpha; - if (u > 0) { // if the pixel is not fully transparent/black - byte |= onebit; // turn on the corresponding bit of the bitmap - } - onebit = onebit << 1; // move the single set bit one position to the left - if (onebit == 0 || j == w-1) { - onebit = 1; - *p++ = ~byte; // store in bitmap one pack of bits, complemented - byte = 0; - } - alpha += d; // point to alpha value of next img pixel - } - } - cairo_surface_t *mask_surf = cairo_image_surface_create_for_data(bits, CAIRO_FORMAT_A1, - w, h, bytesperrow); - shape_data_->mask_pattern_ = cairo_pattern_create_for_surface(mask_surf); - cairo_surface_destroy(mask_surf); - shape_data_->shape_ = img; - shape_data_->lw_ = w; - shape_data_->lh_ = h; -} - - -void Fl_Wayland_Window_Driver::shape(const Fl_Image* img) { - if (shape_data_) { - if (shape_data_->mask_pattern_) { - cairo_surface_t *surface; - cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface); - uchar *data = cairo_image_surface_get_data(surface); - cairo_pattern_destroy(shape_data_->mask_pattern_); - delete[] data; - } - } - else { - shape_data_ = new shape_data_type; - } - memset(shape_data_, 0, sizeof(shape_data_type)); - pWindow->border(false); - int d = img->d(); - if (d && img->count() >= 2) { - shape_pixmap_((Fl_Image*)img); - shape_data_->shape_ = (Fl_Image*)img; - } - else if (d == 0) shape_bitmap_((Fl_Image*)img); - else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); - else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); -} - - -void Fl_Wayland_Window_Driver::draw_end() -{ - if (shape_data_ && shape_data_->mask_pattern_) { - Fl_Wayland_Graphics_Driver *gr_dr = (Fl_Wayland_Graphics_Driver*)fl_graphics_driver; - cairo_t *cr = gr_dr->cr(); - cairo_matrix_t matrix; - cairo_matrix_init_scale(&matrix, double(shape_data_->lw_) / (pWindow->w() + 1), - double(shape_data_->lh_) / (pWindow->h() + 1) ); - cairo_matrix_translate(&matrix, 1, 1); - cairo_pattern_set_matrix(shape_data_->mask_pattern_, &matrix); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_mask(cr, shape_data_->mask_pattern_); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - } -} - - -/* Returns images of the captures of the window title-bar, and the left, bottom and right window borders - (or NULL if a particular border is absent). - Returned images can be deleted after use. Their depth and size may be platform-dependent. - The top and bottom images extend from left of the left border to right of the right border. - */ -void Fl_Wayland_Window_Driver::capture_titlebar_and_borders(Fl_RGB_Image*& top, - Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) -{ - top = left = bottom = right = NULL; - if (pWindow->decorated_h() == h()) return; - int htop = pWindow->decorated_h() - pWindow->h(); - struct wld_window *wwin = fl_wl_xid(pWindow); - int width, height, stride; - uchar *cairo_data = fl_libdecor_titlebar_buffer(wwin->frame, &width, &height, &stride); - if (!cairo_data) return; - uchar *data = new uchar[width * height * 4]; - uchar *p = data; - for (int j = 0; j < height; j++) { - uchar *q = cairo_data + j * stride; - for (int i = 0; i < width; i++) { - *p++ = *(q+2); // R - *p++ = *(q+1); // G - *p++ = *q; // B - *p++ = *(q+3); // A - q += 4; - } - } - top = new Fl_RGB_Image(data, width, height, 4); - top->alloc_array = 1; - top->scale(pWindow->w(), htop); -} - - -// make drawing go into this window (called by subclass flush() impl.) -void Fl_Wayland_Window_Driver::make_current() { - if (!shown()) { - static const char err_message[] = "Fl_Window::make_current(), but window is not shown()."; - fl_alert(err_message); - Fl::fatal(err_message); - } - - struct wld_window *window = fl_wl_xid(pWindow); - if (window->buffer) { - ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag( - &window->buffer->draw_buffer_needs_commit); - } - - // to support progressive drawing - if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb) && - (!wait_for_expose_value) ) { - Fl_Wayland_Graphics_Driver::buffer_commit(window); - } - - Fl_Wayland_Window_Driver::wld_window = window; - fl_window = (Window)window; - float f = Fl::screen_scale(pWindow->screen_num()); - int wld_s = wld_scale(); - if (!window->buffer) { - window->buffer = Fl_Wayland_Graphics_Driver::create_wld_buffer( - int(pWindow->w() * f) * wld_s, int(pWindow->h() * f) * wld_s, false); - ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag( - &window->buffer->draw_buffer_needs_commit); - } - ((Fl_Wayland_Graphics_Driver*)fl_graphics_driver)->set_cairo( - window->buffer->draw_buffer.cairo_, f * wld_s); - ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->wld_scale = wld_s; - int *poffset = Fl_Window_Driver::menu_offset_y(pWindow); - if (poffset) { // for tall menu windows under KWIN to offset drawing inside window - cairo_translate(window->buffer->draw_buffer.cairo_, 0, *poffset); - } - cairo_rectangle_int_t *extents = subRect(); - if (extents) { // make damage-to-buffer not to leak outside parent - Fl_Region clip_region = fl_graphics_driver->XRectangleRegion(extents->x, extents->y, - extents->width, extents->height); -//printf("make_current: %dx%d %dx%d\n",extents->x, extents->y, extents->width, extents->height); - Fl_X::flx(pWindow)->region = clip_region; - } - else fl_graphics_driver->clip_region(0); - -#ifdef FLTK_HAVE_CAIROEXT - // update the cairo_t context - if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow); -#endif -} - - -void Fl_Wayland_Window_Driver::flush() { - if (!pWindow->damage()) return; - if (pWindow->as_gl_window()) { - int W = pWindow->w(); - int H = pWindow->h(); - float scale = fl_graphics_driver->scale(); - Fl_Wayland_Window_Driver::in_flush_ = true; - Fl_Window_Driver::flush(); - Fl_Wayland_Window_Driver::in_flush_ = false; - gl_plugin()->do_swap(pWindow); // useful only for GL win with overlay - if (scale != fl_graphics_driver->scale() || W != pWindow->w() || H != pWindow->h()) { - gl_plugin()->invalidate(pWindow); - } - return; - } - struct wld_window *window = fl_wl_xid(pWindow); - if (!window || !window->configured_width) return; - - Fl_X *ip = Fl_X::flx(pWindow); - cairo_region_t* r = (cairo_region_t*)ip->region; - if (!window->buffer || pWindow->as_overlay_window()) r = NULL; - - Fl_Wayland_Window_Driver::in_flush_ = true; - Fl_Window_Driver::flush(); - Fl_Wayland_Window_Driver::in_flush_ = false; - if (!window->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(window, r); -} - - -void Fl_Wayland_Window_Driver::show() { - if (!shown()) { - fl_open_display(); - makeWindow(); - } else { - // Wayland itself gives no way to programmatically unminimize a minimized window - Fl::handle(FL_SHOW, pWindow); - } -} - - -static void popup_done(void *data, struct xdg_popup *xdg_popup); - - -static void destroy_surface_caution_pointer_focus(struct wl_surface *surface, - struct Fl_Wayland_Screen_Driver::seat *seat) { - if (seat->pointer_focus == surface) seat->pointer_focus = NULL; - if (seat->keyboard_surface == surface) seat->keyboard_surface = NULL; - wl_surface_destroy(surface); -} - - -void Fl_Wayland_Window_Driver::hide() { - if (pWindow == Fl_Screen_Driver::transient_scale_parent) { - // Delete also the running transient scale window - // because the transient is a popup and MUST be deleted - // before its parent. - Fl::remove_timeout(Fl_Screen_Driver::del_transient_window); - Fl_Screen_Driver::del_transient_window(NULL); - } - Fl_X* ip = Fl_X::flx(pWindow); - if (hide_common()) return; - if (ip->region) { - Fl_Graphics_Driver::default_driver().XDestroyRegion(ip->region); - ip->region = 0; - } - screen_num_ = -1; - struct wld_window *wld_win = (struct wld_window*)ip->xid; - if (wld_win) { // this test makes sure ip->xid has not been destroyed already - Fl_Wayland_Graphics_Driver::buffer_release(wld_win); - if (wld_win->kind == SUBWINDOW && wld_win->subsurface) { - wl_subsurface_destroy(wld_win->subsurface); - wld_win->subsurface = NULL; - } -#if HAVE_XDG_DIALOG - if (wld_win->xdg_dialog) { - xdg_dialog_v1_destroy(wld_win->xdg_dialog); - wld_win->xdg_dialog = NULL; - } -#endif - if (wld_win->kind == DECORATED) { - libdecor_frame_unref(wld_win->frame); - wld_win->frame = NULL; - wld_win->xdg_surface = NULL; - } else { - if (wld_win->kind == POPUP && wld_win->xdg_popup) { - popup_done(xdg_popup_get_user_data(wld_win->xdg_popup), wld_win->xdg_popup); - wld_win->xdg_popup = NULL; - } - if (wld_win->kind == UNFRAMED && wld_win->xdg_toplevel) { - xdg_toplevel_destroy(wld_win->xdg_toplevel); - wld_win->xdg_toplevel = NULL; - } - if (wld_win->xdg_surface) { - xdg_surface_destroy(wld_win->xdg_surface); - wld_win->xdg_surface = NULL; - } - } - if (wld_win->custom_cursor) delete_cursor(wld_win->custom_cursor); - if (wld_win->wl_surface) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - destroy_surface_caution_pointer_focus(wld_win->wl_surface, scr_driver->seat); - wld_win->wl_surface = NULL; - } - while (!wl_list_empty(&wld_win->outputs)) { // remove from screens where it belongs - struct surface_output *s_output; - s_output = wl_container_of(wld_win->outputs.next, s_output, link); - wl_list_remove(&s_output->link); - free(s_output); - } - if (Fl_Wayland_Window_Driver::wld_window == wld_win) { - Fl_Wayland_Window_Driver::wld_window = NULL; - } - if (wld_win->frame_cb) wl_callback_destroy(wld_win->frame_cb); // useful for GL subwins - free(wld_win); - } - delete ip; -} - - -void Fl_Wayland_Window_Driver::map() { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - if (wl_win->kind == SUBWINDOW && !wl_win->subsurface) { - struct wld_window *parent = fl_wl_xid(pWindow->window()); - if (parent) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - wl_win->subsurface = wl_subcompositor_get_subsurface(scr_driver->wl_subcompositor, - wl_win->wl_surface, parent->wl_surface); - float f = Fl::screen_scale(pWindow->top_window()->screen_num()); - wl_subsurface_set_position(wl_win->subsurface, pWindow->x() * f, pWindow->y() * f); - wl_subsurface_set_desync(wl_win->subsurface); // important - wl_subsurface_place_above(wl_win->subsurface, parent->wl_surface); - wl_win->configured_width = pWindow->w(); - wl_win->configured_height = pWindow->h(); - wait_for_expose_value = 0; - pWindow->redraw(); - } - } -} - - -void Fl_Wayland_Window_Driver::unmap() { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - if (wl_win->kind == SUBWINDOW && wl_win->wl_surface) { - wl_surface_attach(wl_win->wl_surface, NULL, 0, 0); - Fl_Wayland_Graphics_Driver::buffer_release(wl_win); - wl_subsurface_destroy(wl_win->subsurface); - wl_win->subsurface = NULL; - } -} - - -void Fl_Wayland_Window_Driver::size_range() { - if (shown()) { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - float f = Fl::screen_scale(pWindow->screen_num()); - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (wl_win->kind == DECORATED && wl_win->frame) { - int X,Y,W,H; - Fl::screen_work_area(X,Y,W,H, Fl::screen_num(x(),y(),w(),h())); - if (maxw && maxw < W && maxh && maxh < H) { - libdecor_frame_unset_capabilities(wl_win->frame, LIBDECOR_ACTION_FULLSCREEN); - } else { - libdecor_frame_set_capabilities(wl_win->frame, LIBDECOR_ACTION_FULLSCREEN); - } - if (maxw && maxh && (minw >= maxw || minh >= maxh)) { - libdecor_frame_unset_capabilities(wl_win->frame, LIBDECOR_ACTION_RESIZE); - } else { - libdecor_frame_set_capabilities(wl_win->frame, LIBDECOR_ACTION_RESIZE); - } - libdecor_frame_set_min_content_size(wl_win->frame, minw*f, minh*f); - libdecor_frame_set_max_content_size(wl_win->frame, maxw*f, maxh*f); - if (xdg_toplevel()) { - struct libdecor_state *state = libdecor_state_new(int(w() * f), int(h() * f)); - libdecor_frame_commit(wl_win->frame, state, NULL); - libdecor_state_free(state); - } - } else if (wl_win->kind == UNFRAMED && wl_win->xdg_toplevel) { - xdg_toplevel_set_min_size(wl_win->xdg_toplevel, minw*f, minh*f); - if (maxw && maxh) - xdg_toplevel_set_max_size(wl_win->xdg_toplevel, maxw*f, maxh*f); - } - } -} - - -void Fl_Wayland_Window_Driver::iconize() { - Fl_X* ip = Fl_X::flx(pWindow); - struct wld_window *wl_win = (struct wld_window*)ip->xid; - if (wl_win->kind == DECORATED) { - libdecor_frame_set_minimized(wl_win->frame); - if (xdg_toplevel_get_version(xdg_toplevel()) < 6) { - Fl::handle(FL_HIDE, pWindow); - } - } - else if (wl_win->kind == UNFRAMED && wl_win->xdg_toplevel) xdg_toplevel_set_minimized(wl_win->xdg_toplevel); -} - - -void Fl_Wayland_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom) -{ - struct wld_window *xid = (struct wld_window*)fl_xid(pWindow); - if (xid && xid->kind == DECORATED) { - libdecor_frame_translate_coordinate(xid->frame, 0, 0, left, top); - *right = *left; - *bottom = 0; - } else { - Fl_Window_Driver::decoration_sizes(top, left, right, bottom); - } -} - - -int Fl_Wayland_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h, - int dest_x, int dest_y, - void (*draw_area)(void*, int,int,int,int), void* data) -{ - struct wld_window * xid = fl_wl_xid(pWindow); - struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = xid->buffer; - float s = wld_scale() * fl_graphics_driver->scale(); - if (s != 1) { - src_x = src_x * s; - src_y = src_y * s; - src_w = src_w * s; - src_h = src_h * s; - dest_x = dest_x * s; - dest_y = dest_y * s; - } - if (src_x == dest_x) { // vertical scroll - int i, to, step; - if (src_y > dest_y) { - i = 0; to = src_h; step = 1; - } else { - i = src_h - 1; to = -1; step = -1; - } - while (i != to) { - memcpy( - buffer->draw_buffer.buffer + (dest_y + i) * buffer->draw_buffer.stride + 4 * dest_x, - buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * src_x, - 4 * src_w); - i += step; - } - } else { // horizontal scroll - int i, to, step; - if (src_x > dest_x) { - i = 0; to = src_h; step = 1; - } else { - i = src_h - 1; to = -1; step = -1; - } - while (i != to) { - memmove( - buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * dest_x, - buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * src_x, - 4 * src_w); - i += step; - } - } - return 0; -} - - -static void handle_error(struct libdecor *libdecor_context, enum libdecor_error error, const char *message) -{ - Fl::fatal("Caught error (%d): %s\n", error, message); -} - - -static struct libdecor_interface libdecor_iface = { - .error = handle_error, -}; - - - -static void delayed_rescale(Fl_Window *win) { - Fl_Window_Driver::driver(win)->is_a_rescale(true); - win->size(win->w(), win->h()); - Fl_Window_Driver::driver(win)->is_a_rescale(false); -} - - -void change_scale(Fl_Wayland_Screen_Driver::output *output, struct wld_window *window, - float pre_scale) { - Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win); - if (!window->fl_win->parent()) { - // for top-level, set its screen number when the 1st screen for this surface changes - Fl_Wayland_Screen_Driver::output *running_output; - Fl_Wayland_Screen_Driver *scr_dr = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - int i = 0; - wl_list_for_each(running_output, &scr_dr->outputs, link) { // each screen of the system - if (running_output == output) { // we've found our screen of the system - win_driver->screen_num(i); - break; - } - i++; - } - } - float post_scale = Fl::screen_scale(win_driver->screen_num()) * output->wld_scale; -//printf("pre_scale=%.1f post_scale=%.1f\n", pre_scale, post_scale); - if (post_scale != pre_scale) { - if (window->kind == Fl_Wayland_Window_Driver::POPUP) { - Fl_Wayland_Graphics_Driver::buffer_release(window); - window->fl_win->redraw(); - } else { - // delaying the rescaling is necessary to set first the window's size_range according to the new screen - Fl::add_timeout(0, (Fl_Timeout_Handler)delayed_rescale, window->fl_win); - } - } -} - - -static void surface_enter(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) { - struct wld_window *window = (struct wld_window*)data; - - if (!Fl_Wayland_Screen_Driver::own_output(wl_output)) - return; - - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output); - if (output == NULL) - return; - - bool list_was_empty = wl_list_empty(&window->outputs); - struct Fl_Wayland_Window_Driver::surface_output *surface_output = - (struct Fl_Wayland_Window_Driver::surface_output*)malloc( - sizeof(struct Fl_Wayland_Window_Driver::surface_output)); - surface_output->output = output; - // add to end of the linked list of displays of this surface - struct wl_list *e = &window->outputs; - while (e->next != &window->outputs) e = e->next; // move e to end of linked list - wl_list_insert(e, &surface_output->link); -//printf("window %p enters screen id=%d length=%d\n", window->fl_win, output->id, wl_list_length(&window->outputs)); - if (list_was_empty && !window->fl_win->parent()) { - change_scale(output, window, 0); - } -} - - -static void surface_leave(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) { - if (!Fl_Wayland_Screen_Driver::own_output(wl_output)) - return; - struct wld_window *window = (struct wld_window*)data; - Fl_Wayland_Screen_Driver::output *output = - (Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output); - Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win); - float pre_scale = Fl::screen_scale(win_driver->screen_num()) * win_driver->wld_scale(); - struct Fl_Wayland_Window_Driver::surface_output *s_output; - int count = 0; - wl_list_for_each(s_output, &window->outputs, link) { - count++; - if (s_output->output == output) { - wl_list_remove(&s_output->link); - free(s_output); -//printf("window %p leaves screen id=%d length=%d\n", window->fl_win, output->id, wl_list_length(&window->outputs)); - break; - } - } - if (count == 1 && !wl_list_empty(&window->outputs) && !window->fl_win->parent()) { - s_output = wl_container_of(window->outputs.next, s_output, link); - change_scale(s_output->output, window, pre_scale); - } -} - - -static struct wl_surface_listener surface_listener = { - surface_enter, - surface_leave, -}; - - -Fl_Window *Fl_Wayland_Window_Driver::surface_to_window(struct wl_surface *surface) { - if (surface) { - if (wl_proxy_get_listener((struct wl_proxy *)surface) == &surface_listener) { - return ((struct wld_window *)wl_surface_get_user_data(surface))->fl_win; - } - } - return NULL; -} - - -static struct Fl_Wayland_Screen_Driver::output *screen_num_to_output(int num_screen) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - int i = 0; - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &(scr_driver->outputs), link) { // all screens of the system - if (i++ == num_screen) return output; - } - return NULL; -} - - -#define LIBDECOR_MR131 1 // this means libdecor does not include MR!131 yet - -#ifdef LIBDECOR_MR131 -/* === Beginning of hack that would become un-needed if libdecor accepted MR!131 === */ - -// true while the GUI is interactively resizing a decorated window -static bool in_decorated_window_resizing = false; - - -// libdecor's configure cb function for xdg_toplevel objects -static void (*decor_xdg_toplevel_configure)(void*, struct xdg_toplevel *, int32_t, - int32_t, struct wl_array *); - - -static void fltk_xdg_toplevel_configure(void *user_data, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, - struct wl_array *states) { - uint32_t *p; - in_decorated_window_resizing = false; - // Replace wl_array_for_each(p, states) rejected by C++ - for (p = (uint32_t *)(states)->data; - (const char *) p < ((const char *) (states)->data + (states)->size); - (p)++) { - if (*p == XDG_TOPLEVEL_STATE_RESIZING) { - in_decorated_window_resizing = true; - break; - } - } - decor_xdg_toplevel_configure(user_data, xdg_toplevel, width, height, states); -} - - -struct wl_object { // copied from wayland-private.h - const struct wl_interface *interface; - const void *implementation; - uint32_t id; -}; - - -// replace libdecor's toplevel configure cb by FLTK's -static void use_FLTK_toplevel_configure_cb(struct libdecor_frame *frame) { - struct wl_object *object = (struct wl_object *)libdecor_frame_get_xdg_toplevel(frame); - static struct xdg_toplevel_listener *fltk_listener = NULL; - if (!fltk_listener) { - struct xdg_toplevel_listener *decor_listener = (struct xdg_toplevel_listener*) - object->implementation; - fltk_listener = (struct xdg_toplevel_listener*) - malloc(sizeof(struct xdg_toplevel_listener)); - // initialize FLTK's listener with libdecor's values - *fltk_listener = *decor_listener; - // memorize libdecor's toplevel configure cb - decor_xdg_toplevel_configure = decor_listener->configure; - // replace libdecor's toplevel configure cb by FLTK's - fltk_listener->configure = fltk_xdg_toplevel_configure; - } - // replace the toplevel listener by a copy whose configure member is FLTK's - object->implementation = fltk_listener; -} - -/* === End of hack that would become un-needed if libdecor accepted MR!131 === */ -#endif // LIBDECOR_MR131 - - -// does win entirely cover its parent ? -static void does_window_cover_parent(Fl_Window *win) { - Fl_Window *parent = win->window(); - fl_wl_xid(parent)->covered = (win->x() <= 0 && win->y() <= 0 && - win->w() >= parent->w() && win->h() >= parent->h()); -} - - -// recursively explore all shown subwindows in a window and call f for each -static void scan_subwindows(Fl_Group *g, void (*f)(Fl_Window *)) { - for (int i = 0; i < g->children(); i++) { - Fl_Widget *o = g->child(i); - if (o->as_window()) { - if (!o->as_window()->shown()) continue; - f(o->as_window()); - } - if (o->as_group()) scan_subwindows(o->as_group(), f); - } -} - -// Generate FL_APP_ACTIVATE and FL_APP_DEACTIVATE events -static bool app_has_active_window = false; - -// If a window is deactivated, check after a short delay if any other window has -// become active. If not, send an FL_APP_DEACTIVATE event. -static void deferred_check_app_deactivate(void*) { - if (!app_has_active_window) return; - app_has_active_window = false; - // Check all FLTK windows to see if any are still active - for (Fl_Window *w = Fl::first_window(); w; w = Fl::next_window(w)) { - if (w->visible_r()) { - struct wld_window* xid = fl_wl_xid(w); - if (xid && (xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - app_has_active_window = true; - break; - } - } - } - if (!app_has_active_window) Fl::handle(FL_APP_DEACTIVATE, nullptr); -} - -static void handle_configure(struct libdecor_frame *frame, - struct libdecor_configuration *configuration, void *user_data) -{ - struct wld_window *window = (struct wld_window*)user_data; - if (!window->wl_surface) return; - int width, height; - enum libdecor_window_state window_state; - struct libdecor_state *state; - Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(window->fl_win); - // true exactly for the 1st run of handle_configure() for this window - bool is_1st_run = (window->xdg_surface == 0); - // true exactly for the 2nd run of handle_configure() for this window - bool is_2nd_run = (window->xdg_surface != 0 && driver->wait_for_expose_value); - float f = Fl::screen_scale(window->fl_win->screen_num()); - - if (!window->xdg_surface) window->xdg_surface = libdecor_frame_get_xdg_surface(frame); - -#ifdef LIBDECOR_MR131 - if (is_1st_run) use_FLTK_toplevel_configure_cb(frame); -#endif - struct wl_output *wl_output = NULL; - if (window->fl_win->fullscreen_active()) { - if (!(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) { - if (Fl_Window_Driver::driver(window->fl_win)->force_position()) { - struct Fl_Wayland_Screen_Driver::output *output = - screen_num_to_output(window->fl_win->screen_num()); - if (output) wl_output = output->wl_output; - } - libdecor_frame_set_fullscreen(window->frame, wl_output); - } - } else if (driver->show_iconic()) { - libdecor_frame_set_minimized(window->frame); - driver->show_iconic(0); - } - if (!libdecor_configuration_get_window_state(configuration, &window_state)) - window_state = LIBDECOR_WINDOW_STATE_NONE; - if ((window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) && - !(window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) && !window->fl_win->border()) { - // necessary so Mutter correctly positions borderless window back from fullscreen - window->fl_win->redraw(); - } - window->state = window_state; - - // Weston, KWin, and some old versions of Mutter, on purpose, don't set the - // window width x height when xdg_toplevel_configure runs twice - // during resizable window creation - // (see https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/6). - // Consequently, libdecor_configuration_get_content_size() may return false twice. - // Weston and KWin, at least, don't change the window size asked by the client application - // which is available here in floating_{width,height}. - if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) { - if (is_2nd_run) { - width = window->floating_width; - height = window->floating_height; - if (!driver->is_resizable()) { - libdecor_frame_set_min_content_size(frame, width, height); - libdecor_frame_set_max_content_size(frame, width, height); - } - } else { width = height = 0; } - } - if (is_2nd_run && Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::MUTTER) { - scan_subwindows(window->fl_win, does_window_cover_parent); // issue #878 - } - - if (window->fl_win->fullscreen_active() && - Fl_Window_Driver::driver(window->fl_win)->force_position()) { - int X, Y, W, H; - Fl::screen_xywh(X, Y, W, H, window->fl_win->screen_num()); - width = W * f; height = H * f; - } - - if (width == 0) { - width = window->floating_width; - height = window->floating_height; - //fprintf(stderr,"handle_configure: using floating %dx%d\n",width,height); - } - -#ifndef LIBDECOR_MR131 - bool in_decorated_window_resizing = (window->state & LIBDECOR_WINDOW_STATE_RESIZING); -#endif - bool condition = in_decorated_window_resizing; - if (condition) { // see issue #878 - condition = (window->covered ? (window->buffer && window->buffer->in_use) : (window->frame_cb != NULL)); - } - if (condition) { - // Skip resizing & redrawing. The last resize request won't be skipped because - // LIBDECOR_WINDOW_STATE_RESIZING will be off or cb will be NULL then. - return; - } - - driver->in_handle_configure = true; - window->fl_win->resize(0, 0, ceil(width / f), ceil(height / f)); - driver->in_handle_configure = false; - if (wl_output) window->fl_win->redraw(); - window->configured_width = ceil(width / f); - window->configured_height = ceil(height / f); - if (is_2nd_run) driver->wait_for_expose_value = 0; -//fprintf(stderr, "handle_configure fl_win=%p size:%dx%d state=%x wait_for_expose_value=%d is_2nd_run=%d\n", window->fl_win, width,height,window_state,driver->wait_for_expose_value, is_2nd_run); - - // When no window is active, and one window gets activated, generate an FL_APP_ACTIVATE event - if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) { - if (!app_has_active_window) { - app_has_active_window = true; - Fl::handle(FL_APP_ACTIVATE, nullptr); - } - - if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON) { - // After click on titlebar, weston calls wl_keyboard_enter() for a - // titlebar-related surface that FLTK can't identify, so we send FL_FOCUS here. - Fl::handle(FL_FOCUS, window->fl_win); - } - if (!window->fl_win->border()) libdecor_frame_set_visibility(window->frame, false); - else if (!libdecor_frame_is_visible(window->frame)) { - libdecor_frame_set_visibility(window->frame, true); - } else if (!window->fl_win->visible()) { - Fl::handle(FL_SHOW, window->fl_win); // useful when un-minimizing - } - } else if (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) { // window is minimized - Fl::handle(FL_HIDE, window->fl_win); - } - - // When a window gets deactivated and there are no other active windows, - // generate an FL_APP_DEACTIVATE event - if ( ((window_state & LIBDECOR_WINDOW_STATE_ACTIVE) == 0) && app_has_active_window) { - Fl::add_timeout(0.1, deferred_check_app_deactivate, nullptr); - } - - if (window->fl_win->border()) - driver->is_maximized(window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED); - if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) state = libdecor_state_new(width, - height); - else state = libdecor_state_new(int(ceil(width/f)*f), int(ceil(height/f)*f)); - libdecor_frame_commit(frame, state, configuration); - if (libdecor_frame_is_floating(frame)) { // store floating dimensions - window->floating_width = int(ceil(width/f)*f); - window->floating_height = int(ceil(height/f)*f); - //fprintf(stderr,"set floating_width+height %dx%d\n",width,height); - } - libdecor_state_free(state); - - driver->flush(); - if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::WESTON || !is_1st_run) { - window->fl_win->clear_damage(); - } -} - - -void Fl_Wayland_Window_Driver::wait_for_expose() -{ - Fl_Window_Driver::wait_for_expose(); - struct wld_window * xid = fl_wl_xid(pWindow); - if (!xid) return; - if (pWindow->fullscreen_active()) { - if (xid->kind == DECORATED) { - while (!(xid->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) || - !(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - } - } else if (xid->kind == UNFRAMED) { - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - } - } else if (xid->kind == DECORATED) { - // necessary for the windowfocus demo program with recent Wayland versions - if (!(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display); - } - } -} - - -static void delayed_close(Fl_Window *win) { - Fl::remove_check((Fl_Timeout_Handler)delayed_close, win); - Fl::handle(FL_CLOSE, win); -} - - -static void handle_close(struct libdecor_frame *frame, void *user_data) -{ // runs when the close button of a window titlebar is pushed - // or after "Quit" of the application menu - // or after the Kill command of Sway - Fl_Window* win = ((struct wld_window*)user_data)->fl_win; - int X, Y; - libdecor_frame_translate_coordinate(frame, 0, 0, &X, &Y); - if (Y == 0) Fl::handle(FL_CLOSE, win); - else { - // the close window attempt is delayed because libdecor - // uses the frame after return from this function - Fl::add_check((Fl_Timeout_Handler)delayed_close, win); - } -} - - -static void handle_commit(struct libdecor_frame *frame, void *user_data) -{ - struct wld_window* wl_win = (struct wld_window*)user_data; - if (wl_win->wl_surface) wl_surface_commit(wl_win->wl_surface); -} - - -static void handle_dismiss_popup(struct libdecor_frame *frame, const char *seat_name, void *user_data) -{ -} - - -static struct libdecor_frame_interface libdecor_frame_iface = { - handle_configure, - handle_close, - handle_commit, - handle_dismiss_popup, -}; - - -static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) -{ - // runs for borderless windows and popup (menu,tooltip) windows - struct wld_window *window = (struct wld_window*)data; - xdg_surface_ack_configure(xdg_surface, serial); -//fprintf(stderr, "xdg_surface_configure: surface=%p\n", window->wl_surface); - - if (window->fl_win->w() != window->configured_width || - window->fl_win->h() != window->configured_height) { - if (window->buffer) { - Fl_Wayland_Graphics_Driver::buffer_release(window); - } - } - window->configured_width = window->fl_win->w(); - window->configured_height = window->fl_win->h(); - Fl_Window_Driver::driver(window->fl_win)->flush(); - window->fl_win->clear_damage(); -} - - -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_configure, -}; - - -static bool parse_states_fullscreen(struct wl_array *states) -{ - uint32_t *p; - // Replace wl_array_for_each(p, states) rejected by C++ - for (p = (uint32_t *)(states)->data; - (const char *) p < ((const char *) (states)->data + (states)->size); - (p)++) { - if (*p == XDG_TOPLEVEL_STATE_FULLSCREEN) return true; - } - return false; -} - - -static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, struct wl_array *states) -{ - // runs for borderless top-level windows - // under Weston: width & height are 0 during both calls, except if fullscreen - struct wld_window *window = (struct wld_window*)data; -//fprintf(stderr, "xdg_toplevel_configure: surface=%p size: %dx%d\n", window->wl_surface, width, height); - if (window->fl_win->fullscreen_active() && !parse_states_fullscreen(states)) { - struct wl_output *wl_output = NULL; - if (Fl_Window_Driver::driver(window->fl_win)->force_position()) { - struct Fl_Wayland_Screen_Driver::output *output = - screen_num_to_output(window->fl_win->screen_num()); - if (output) wl_output = output->wl_output; - } - xdg_toplevel_set_fullscreen(xdg_toplevel, wl_output); - if (wl_output) { - int X, Y; - Fl::screen_xywh(X, Y, width, height, window->fl_win->screen_num()); - } - } - if (window->configured_width) { - Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0; - } - float f = Fl::screen_scale(window->fl_win->screen_num()); - if (width == 0 || height == 0) { - width = window->fl_win->w() * f; - height = window->fl_win->h() * f; - } - window->fl_win->size(ceil(width / f), ceil(height / f)); - if (window->buffer && (ceil(width / f) != window->configured_width || - ceil(height / f) != window->configured_height)) { - Fl_Wayland_Graphics_Driver::buffer_release(window); - } - window->configured_width = ceil(width / f); - window->configured_height = ceil(height / f); -} - - -static void xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel) -{ -} - - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure, - .close = xdg_toplevel_close, -}; - - -struct win_positioner { - struct wld_window *window; - int x, y; - Fl_Window *child_popup; -}; - - -static void popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, - int32_t width, int32_t height) { - struct win_positioner *win_pos = (struct win_positioner *)data; - struct wld_window *window = win_pos->window; -//printf("popup_configure %p asked:%dx%d got:%dx%d\n",window->fl_win, win_pos->x,win_pos->y, x,y); - Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0; - int HH; - Fl_Window_Driver::menu_parent(&HH); - if (window->fl_win->h() > HH && y != win_pos->y) { // A menu taller than the display - // Under KWin, height is set to the display height or less: we ignore that. - window->state = (y - win_pos->y); - // make selected item visible, if there's one - Fl_Window_Driver::scroll_to_selected_item(window->fl_win); - } - if (Fl_Window_Driver::current_menu_button && !Fl_Window_Driver::menu_leftorigin(window->fl_win)) { - int X, Y; - Fl_Window_Driver::current_menu_button->top_window_offset(X, Y); - if (y < Y) { - Fl_Window *win = window->fl_win; - win->Fl_Widget::resize(win->x(), Y - win->h(), win->w(), win->h()); - } - } -} - - -static struct xdg_popup *mem_grabbing_popup = NULL; - - -static void popup_done(void *data, struct xdg_popup *xdg_popup) { - struct win_positioner *win_pos = (struct win_positioner *)data; - struct wld_window *window = win_pos->window; -//fprintf(stderr, "popup_done: popup=%p data=%p xid=%p fl_win=%p\n", xdg_popup, data, window, window->fl_win); - if (win_pos->child_popup) win_pos->child_popup->hide(); - xdg_popup_destroy(xdg_popup); - delete win_pos; - // The sway compositor calls popup_done directly and hides the menu - // when the app looses focus. - // Thus, we hide the window so FLTK and Wayland are in matching states. - window->xdg_popup = NULL; - window->fl_win->hide(); - if (mem_grabbing_popup == xdg_popup) { - mem_grabbing_popup = NULL; - } -} - - -static const struct xdg_popup_listener popup_listener = { - .configure = popup_configure, - .popup_done = popup_done, -}; - - -bool Fl_Wayland_Window_Driver::in_flush_ = false; - - -static const char *get_prog_name() { - pid_t pid = getpid(); - char fname[100]; - snprintf(fname, 100, "/proc/%u/cmdline", pid); - FILE *in = fopen(fname, "r"); - if (in) { - static char line[200]; - const char *p = fgets(line, sizeof(line), in); - fclose(in); - p = strrchr(line, '/'); if (!p) p = line; else p++; - return p; - } - return "unknown"; -} - - -/* Implementation note about menu windows under Wayland. - Wayland offers a way to position popup windows such as menu windows using constraints. - Each popup is located relatively to a parent window which can be a popup itself and - MUST overlap or at least touch this parent. - Constraints determine how a popup is positioned relatively to a defined area (called - the anchor rectangle) of its parent popup/window and what happens when this position - would place the popup all or partly outside the display. - In contrast, FLTK computes the adequate positions of menu windows in the display using - knowledge about the display size and the location of the window in the display, and then - maps them at these positions. - These 2 logics are quite different because Wayland hides the position of windows inside the - display, whereas FLTK uses the location of windows inside the display to position popups. - Let's call "source window" the non-popup window above which all popups are mapped. - The approach implemented here is two-fold. - 1) If a menu window is not taller than the display, use Wayland constraints to position it. - Wayland imposes that the first constructed popup must overlap or touch the source window. - Other popups can be placed below, above, at right, or at left of a previous popup which - allows them to expand outside the source window, while constraints can ensure they won't - extend outside the display. - 2) A menu window taller than the display is initially mapped with the constraint to - begin at the top border of the display. This allows FLTK to know the distance between - the source window and the display top. FLTK can later reposition the same tall popup, - without the constraint not to go beyond the display top, at the exact position so that - the desired series of menu items appear in the visible part of the tall popup. - - In case 1) above, the values that represent the display bounds are given very - large values. That's done by member function Fl_Wayland_Window_Driver::menu_window_area(). - Consequently, FLTK computes an initial layout of future popups relatively to - the source window as if it was mapped on an infinitely large display. Then, the location - of the first popup to be mapped is modified if necessary so it overlaps or touches the - source window. Finally, other popups are located using Wayland logic below, above or to the - right of previous popups. Wayland constraints mechanism also allows a popup tentatively - placed below a previous one to be flipped above it if that prevents the popup from expanding - beyond display limits. This is used to unfold menu bar menus below or above the menu bar. - After each popup is created and scheduled for being mapped on display by function - process_menu_or_tooltip(), makeWindow() calls Fl_Window::wait_for_expose() so its constrained - position is known before computing the position of the next popup. This ensures each - popup is correctly placed relatively to its parent. - - Groups of popups containing a menutitle, the associated menuwindow, and optionally - a submenu window and that don't belong to an Fl_Menu_Bar are mapped in a different order: - the menuwindow is mapped first, and the menutitle is mapped second above it as a child popup. - Fl_Window_Driver::is_floating_title() detects when such a menutitle is created, - static member variable previous_floatingtitle is assigned the value of this menutitle, and - the menutitle is mapped only after the menuwindow has been mapped, as a child of it. - This positions better the popup group in the display relatively to where the popup - was created. - - In case 2) above, a tall popup is mapped with XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y - which puts its top at the display top border. The Wayland system then calls the - popup_configure() callback function with the x,y coordinates of the top left corner - where the popup is mapped relatively to an anchor point in the source window. - The difference between the asked window position and the effective position is stored - in the state member variable of the tall popup's struct wld_window. This information - allows FLTK to compute the distance between the source window top and the display top border. - Function Fl_Wayland_Window_Driver::menu_window_area() sets the top of the display to - a value such that function Fl_Wayland_Window_Driver::reposition_menu_window(), called by - menuwindow::autoscroll(int n), ensures that menu item #n is visible. Static boolean member - variable Fl_Wayland_Window_Driver::new_popup is useful to position tall menuwindows created - by an Fl_Menu_Button or Fl_Choice. It is set to true when any menu popup is created. - It is used each time menu_window_area() runs for a particular Fl_Menu_Button or Fl_Choice, - and is reset to false after its first use. This allows menu_window_area() to give the top of - the display an adequate value the first time and to keep this value next times it runs. - Fl_Window_Driver::scroll_to_selected_item() scrolls the tall popup so its selected - item, when there's one, is visible immediately after the tall popup is mapped on display. - */ - - -bool Fl_Wayland_Window_Driver::process_menu_or_tooltip(struct wld_window *new_window) { - // a menu window or tooltip - new_window->kind = Fl_Wayland_Window_Driver::POPUP; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (Fl_Window_Driver::is_floating_title(pWindow)) { - previous_floatingtitle = pWindow; - return true; - } - new_window->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, - new_window->wl_surface); - xdg_surface_add_listener(new_window->xdg_surface, &xdg_surface_listener, new_window); - Fl_Wayland_Window_Driver::new_popup = true; - Fl_Window *menu_origin = NULL; - if (pWindow->menu_window()) { - menu_origin = Fl_Window_Driver::menu_leftorigin(pWindow); - if (!menu_origin && !previous_floatingtitle) menu_origin = - Fl_Window_Driver::menu_title(pWindow); - } - Fl_Widget *target = (pWindow->tooltip_window() ? Fl_Tooltip::current() : NULL); - if (pWindow->user_data() == &Fl_Screen_Driver::transient_scale_display && - Fl_Screen_Driver::transient_scale_parent) { - target = Fl_Screen_Driver::transient_scale_parent; - } - if (!target) target = Fl_Window_Driver::menu_parent(); - if (!target) target = Fl::belowmouse(); - if (!target) target = Fl::first_window(); - Fl_Window *parent_win = target->top_window(); - while (parent_win && parent_win->menu_window() && driver(parent_win)->popup_window()) { - parent_win = Fl::next_window(parent_win); - } - Fl_Window *origin_win = (menu_origin ? menu_origin : parent_win); - struct wld_window * parent_xid = fl_wl_xid(origin_win); - struct xdg_surface *parent_xdg = parent_xid->xdg_surface; - float f = Fl::screen_scale(parent_win->screen_num()); - //fprintf(stderr, "menu parent_win=%p pos:%dx%d size:%dx%d\n", parent_win, pWindow->x(), pWindow->y(), pWindow->w(), pWindow->h()); -//printf("window=%p menutitle=%p bartitle=%d leftorigin=%p y=%d\n", pWindow, Fl_Window_Driver::menu_title(pWindow), Fl_Window_Driver::menu_bartitle(pWindow), Fl_Window_Driver::menu_leftorigin(pWindow), pWindow->y()); - struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base); - //xdg_positioner_get_version(positioner) <== gives 1 under Debian and Sway - int popup_x, popup_y; - if (Fl_Window_Driver::current_menu_button && !Fl_Window_Driver::menu_leftorigin(pWindow)) { - int X, Y; - Fl_Window_Driver::current_menu_button->top_window_offset(X, Y); - xdg_positioner_set_anchor_rect(positioner, X * f, Y * f, - Fl_Window_Driver::current_menu_button->w() * f, - Fl_Window_Driver::current_menu_button->h() * f); - popup_x = X * f; - popup_y = 0; - if (parent_xid->kind == Fl_Wayland_Window_Driver::DECORATED && !origin_win->fullscreen_active()) - libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, - &popup_x, &popup_y); - } else if (Fl_Window_Driver::menu_title(pWindow) && Fl_Window_Driver::menu_bartitle(pWindow)) { - xdg_positioner_set_anchor_rect(positioner, 0, 0, - Fl_Window_Driver::menu_title(pWindow)->w() * f, - Fl_Window_Driver::menu_title(pWindow)->h() * f); - popup_x = 0; - popup_y = Fl_Window_Driver::menu_title(pWindow)->h() * f; - } else { - popup_x = pWindow->x() * f; popup_y = pWindow->y() * f; - if (popup_x + pWindow->w() * f < 0) popup_x = - pWindow->w() * f; - if (menu_origin) { - popup_x -= menu_origin->x() * f; - popup_y -= menu_origin->y() * f; - } - if (popup_x >= origin_win->w() * f) popup_x = origin_win->w() * f - 1; - if (!Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_bartitle(pWindow) && - !Fl_Window_Driver::menu_leftorigin(pWindow)) { - // prevent first popup from going above the permissible source window - popup_y = fl_max(popup_y, - pWindow->h() * f); - } - if (parent_xid->kind == Fl_Wayland_Window_Driver::DECORATED && !origin_win->fullscreen_active()) - libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, - &popup_x, &popup_y); - xdg_positioner_set_anchor_rect(positioner, popup_x, 0, 1, 1); - popup_y++; - } - int positioner_H = pWindow->h(); - if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KWIN) { - // Under KWIN, limiting the height of the positioner to the work area height - // results in tall popup windows starting at the top of the screen, what we want. - // Unfortunately, we know the work area height exactly only for single-screen systems, - // otherwise FLTK returns work area height == screen height. In that case we estimate - // work area height ≈ screen height - 44. - int V, work_area_H, screen_H; - Fl::screen_work_area(V, V, V, work_area_H, origin_win->screen_num()); - Fl::screen_xywh(V, V, V, screen_H, origin_win->screen_num()); - if (work_area_H == screen_H) work_area_H -= 44; - if (positioner_H > work_area_H) positioner_H = work_area_H; - } - xdg_positioner_set_size(positioner, pWindow->w() * f , positioner_H * f ); - xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT); - xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); - // prevent menuwindow from expanding beyond display limits - int constraint = 0; - int top_menubar = pWindow->y() - - (Fl_Window_Driver::menu_bartitle(pWindow) && Fl_Window_Driver::menu_title(pWindow) ? - Fl_Window_Driver::menu_title(pWindow)->h() : 0); - if ( !(parent_win->fullscreen_active() && - Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::MUTTER && - ((!Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_leftorigin(pWindow)) || - Fl_Window_Driver::menu_bartitle(pWindow)) && top_menubar < 10 && - !Fl_Window_Driver::current_menu_button) - ) { - // Condition above is only to bypass Mutter bug for fullscreen windows (see #1061) - constraint |= (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); - if ((Fl_Window_Driver::current_menu_button || Fl_Window_Driver::menu_bartitle(pWindow)) && - !Fl_Window_Driver::menu_leftorigin(pWindow)) { - constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; - } - xdg_positioner_set_constraint_adjustment(positioner, constraint); - } - if (!(Fl_Window_Driver::menu_title(pWindow) && Fl_Window_Driver::menu_bartitle(pWindow))) { - xdg_positioner_set_offset(positioner, 0, popup_y); - } - new_window->xdg_popup = xdg_surface_get_popup(new_window->xdg_surface, - parent_xdg, positioner); - struct win_positioner *win_pos = new struct win_positioner; - win_pos->window = new_window; - win_pos->x = popup_x; - win_pos->y = popup_y; - win_pos->child_popup = NULL; -//printf("create xdg_popup=%p data=%p xid=%p fl_win=%p\n",new_window->xdg_popup,win_pos,new_window,new_window->fl_win); - xdg_positioner_destroy(positioner); - xdg_popup_add_listener(new_window->xdg_popup, &popup_listener, win_pos); - if (!mem_grabbing_popup) { - mem_grabbing_popup = new_window->xdg_popup; - //xdg_popup_grab(new_window->xdg_popup, scr_driver->get_wl_seat(), scr_driver->get_serial()); - //libdecor_frame_popup_grab(parent_xid->frame, scr_driver->get_seat_name()); - } - wl_surface_commit(new_window->wl_surface); - // put it on same screen as parent_win - this->screen_num(parent_win->screen_num()); - return false; -} - - -void Fl_Wayland_Window_Driver::makeWindow() -{ - Fl_Group::current(0); // get rid of very common user bug: forgot end() - struct wld_window *new_window; - bool is_floatingtitle = false; - wait_for_expose_value = 1; - - if (pWindow->parent() && !pWindow->window()) return; - if (pWindow->parent() && !pWindow->window()->shown()) return; - - if (!pWindow->parent() && !popup_window()) { - x(0); y(0); // toplevel, non-popup windows must have origin at 0,0 - } - new_window = (struct wld_window *)calloc(1, sizeof *new_window); - new_window->fl_win = pWindow; - wl_list_init(&new_window->outputs); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - - new_window->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor); -//printf("makeWindow:%p %s %s\n", pWindow, pWindow->parent()?"SUB":"", pWindow->as_gl_window()?"GL":""); - wl_surface_add_listener(new_window->wl_surface, &surface_listener, new_window); - - if (!shape()) { // rectangular FLTK windows are opaque - struct wl_region *opaque = wl_compositor_create_region(scr_driver->wl_compositor); - wl_region_add(opaque, 0, 0, 1000000, 1000000); - wl_surface_set_opaque_region(new_window->wl_surface, opaque); - wl_region_destroy(opaque); - } - - if (pWindow->user_data() == &Fl_Screen_Driver::transient_scale_display && - Fl::first_window()) { - // put transient scale win at center of top window by making it a tooltip of top - Fl_Screen_Driver::transient_scale_parent = Fl::first_window(); - pWindow->set_tooltip_window(); - set_popup_window(); - pWindow->position( - (Fl_Screen_Driver::transient_scale_parent->w() - pWindow->w())/2 , - (Fl_Screen_Driver::transient_scale_parent->h() - pWindow->h())/2); - } - - if (popup_window()) { // a menu window or tooltip - is_floatingtitle = process_menu_or_tooltip(new_window); - - } else if (pWindow->border() && !pWindow->parent() ) { // a decorated window - new_window->kind = DECORATED; - if (!scr_driver->libdecor_context) - scr_driver->libdecor_context = libdecor_new(Fl_Wayland_Screen_Driver::wl_display, - &libdecor_iface); - new_window->frame = libdecor_decorate(scr_driver->libdecor_context, new_window->wl_surface, - &libdecor_frame_iface, new_window); - // appears in the Gnome desktop menu bar - libdecor_frame_set_app_id(new_window->frame, get_prog_name()); - libdecor_frame_set_title(new_window->frame, pWindow->label()?pWindow->label():""); - if (!is_resizable()) { - libdecor_frame_unset_capabilities(new_window->frame, LIBDECOR_ACTION_RESIZE); - libdecor_frame_unset_capabilities(new_window->frame, LIBDECOR_ACTION_FULLSCREEN); - } - libdecor_frame_map(new_window->frame); - float f = Fl::screen_scale(pWindow->screen_num()); - new_window->floating_width = pWindow->w() * f; - new_window->floating_height = pWindow->h() * f; - - } else if (pWindow->parent()) { // for subwindows (GL or non-GL) - new_window->kind = SUBWINDOW; - struct wld_window *parent = fl_wl_xid(pWindow->window()); - new_window->subsurface = wl_subcompositor_get_subsurface(scr_driver->wl_subcompositor, - new_window->wl_surface, - parent->wl_surface); -//fprintf(stderr, "makeWindow: subsurface=%p\n", new_window->subsurface); - float f = Fl::screen_scale(pWindow->top_window()->screen_num()); - wl_subsurface_set_position(new_window->subsurface, pWindow->x() * f, pWindow->y() * f); - wl_subsurface_set_desync(new_window->subsurface); // important - // Next 5 statements ensure the subsurface will be mapped because: - // "The effect of adding a sub-surface becomes visible on the next time - // the state of the parent surface is applied." - new_window->configured_width = pWindow->w(); - new_window->configured_height = pWindow->h(); - if (!pWindow->as_gl_window()) { - parent->fl_win->wait_for_expose(); - wl_surface_commit(parent->wl_surface); - } - wait_for_expose_value = 0; - pWindow->border(0); - checkSubwindowFrame(); // make sure subwindow doesn't leak outside parent - - } else { // a window without decoration - new_window->kind = UNFRAMED; - new_window->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, - new_window->wl_surface); -//fprintf(stderr, "makeWindow: xdg_wm_base_get_xdg_surface=%p\n", new_window->xdg_surface); - xdg_surface_add_listener(new_window->xdg_surface, &xdg_surface_listener, new_window); - new_window->xdg_toplevel = xdg_surface_get_toplevel(new_window->xdg_surface); - xdg_toplevel_add_listener(new_window->xdg_toplevel, &xdg_toplevel_listener, new_window); - if (pWindow->label()) xdg_toplevel_set_title(new_window->xdg_toplevel, pWindow->label()); - wl_surface_commit(new_window->wl_surface); - pWindow->border(0); - } - - Fl_Window *old_first = Fl::first_window(); - struct wld_window * first_xid = (old_first ? fl_wl_xid(old_first) : NULL); - Fl_X *xp = new Fl_X; - xp->xid = (fl_uintptr_t)new_window; - other_xid = 0; - xp->w = pWindow; - flx(xp); - xp->region = 0; - if (!pWindow->parent()) { - xp->next = Fl_X::first; - Fl_X::first = xp; - } else if (Fl_X::first) { - xp->next = Fl_X::first->next; - Fl_X::first->next = xp; - } else { - xp->next = NULL; - Fl_X::first = xp; - } - - if (pWindow->modal() || pWindow->non_modal()) { - if (pWindow->modal()) Fl::modal_ = pWindow; - if (new_window->kind == DECORATED && first_xid && first_xid->kind == DECORATED) { - if (first_xid->frame) libdecor_frame_set_parent(new_window->frame, first_xid->frame); - } else if (new_window->kind == UNFRAMED && new_window->xdg_toplevel && first_xid) { - Fl_Wayland_Window_Driver *top_dr = Fl_Wayland_Window_Driver::driver(first_xid->fl_win); - if (top_dr->xdg_toplevel()) xdg_toplevel_set_parent(new_window->xdg_toplevel, - top_dr->xdg_toplevel()); - } - if (new_window->kind == DECORATED || new_window->kind == UNFRAMED) { -#if HAVE_XDG_DIALOG - if (scr_driver->xdg_wm_dialog) { - new_window->xdg_dialog = xdg_wm_dialog_v1_get_xdg_dialog(scr_driver->xdg_wm_dialog, xdg_toplevel()); - if (pWindow->modal()) xdg_dialog_v1_set_modal(new_window->xdg_dialog); - } else -#endif - if (scr_driver->seat->gtk_shell && pWindow->modal()) { - // Useful to position modal windows above their parent with "gnome-shell --version" ≤ 45.2, - // useless but harmless with "gnome-shell --version" ≥ 46.0. - struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(scr_driver->seat->gtk_shell, - new_window->wl_surface); - gtk_surface1_set_modal(gtk_surface); - if (gtk_surface1_get_version(gtk_surface) >= GTK_SURFACE1_RELEASE_SINCE_VERSION) - gtk_surface1_release(gtk_surface); // very necessary - else - gtk_surface1_destroy(gtk_surface); - } - } - } - - size_range(); - pWindow->set_visible(); - int old_event = Fl::e_number; - pWindow->redraw(); - pWindow->handle(Fl::e_number = FL_SHOW); // get child windows to appear - Fl::e_number = old_event; - if (pWindow->menu_window() && popup_window() && !is_floatingtitle) { - // make sure each menu window is mapped with its constraints before mapping next popup - pWindow->wait_for_expose(); - if (previous_floatingtitle) { // a menuwindow with a menutitle - //puts("previous_floatingtitle"); - int HH; - Fl_Window_Driver::menu_parent(&HH); - if (pWindow->h() > HH) { - // a tall menuwindow with a menutitle: don't create the menutitle at all - // and undo what has been created/allocated before - struct wld_window *xid = fl_wl_xid(previous_floatingtitle); - destroy_surface_caution_pointer_focus(xid->wl_surface, scr_driver->seat); - free(xid); - Fl_Window_Driver::driver(previous_floatingtitle)->hide_common(); - previous_floatingtitle = NULL; - return; - } - // map the menutitle popup now as child of pWindow - struct wld_window *xid = fl_wl_xid(previous_floatingtitle); - xid->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, xid->wl_surface); - xdg_surface_add_listener(xid->xdg_surface, &xdg_surface_listener, xid); - struct xdg_positioner *positioner = - xdg_wm_base_create_positioner(scr_driver->xdg_wm_base); - xdg_positioner_set_anchor_rect(positioner, 0, 0, 1, 1); - int snum = Fl_Window_Driver::menu_parent()->screen_num(); - float f = Fl::screen_scale(snum); - // put it on same screen as parent menu - Fl_Window_Driver::driver(previous_floatingtitle)->screen_num(snum); - xdg_positioner_set_size(positioner, previous_floatingtitle->w() * f , - previous_floatingtitle->h() * f ); - xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); - xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_TOP_RIGHT); - xid->xdg_popup = xdg_surface_get_popup(xid->xdg_surface, new_window->xdg_surface, - positioner); - xdg_positioner_destroy(positioner); - struct win_positioner *win_pos = new struct win_positioner; - win_pos->window = xid; - win_pos->x = 0; - win_pos->y = 0; - win_pos->child_popup = NULL; - xdg_popup_add_listener(xid->xdg_popup, &popup_listener, win_pos); - wl_surface_commit(xid->wl_surface); - struct win_positioner *parent_win_pos = - (struct win_positioner*)xdg_popup_get_user_data(new_window->xdg_popup); - parent_win_pos->child_popup = previous_floatingtitle; - previous_floatingtitle = NULL; - } - } - if (pWindow->fullscreen_active()) Fl::handle(FL_FULLSCREEN, pWindow); -} - - -int Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor c) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); -#if HAVE_CURSOR_SHAPE - if (scr_driver->wp_cursor_shape_device) { - if (xid->custom_cursor) { - delete_cursor(xid->custom_cursor); - xid->custom_cursor = NULL; - } - if (c == FL_CURSOR_NONE) return 0; - standard_cursor_ = c; - Fl_Wayland_Screen_Driver::do_set_cursor(scr_driver->seat, NULL, c); - return 1; - } -#endif // HAVE_CURSOR_SHAPE - if (!scr_driver->seat->cursor_theme) return 1; - // Cursor names are the files of directory /usr/share/icons/XXXX/cursors/ - // where XXXX is the name of the current 'cursor theme'. - static struct cursor_file_struct { - Fl_Cursor c; - const char *fname; - Fl_Wayland_Screen_Driver::cursor_shapes wld_c; - } cursor_file_array[] = { - {FL_CURSOR_ARROW, "left_ptr", Fl_Wayland_Screen_Driver::arrow }, - {FL_CURSOR_CROSS, "cross", Fl_Wayland_Screen_Driver::cross }, - {FL_CURSOR_WAIT, "watch", Fl_Wayland_Screen_Driver::wait }, - {FL_CURSOR_INSERT, "xterm", Fl_Wayland_Screen_Driver::insert }, - {FL_CURSOR_HAND, "hand1", Fl_Wayland_Screen_Driver::hand }, - {FL_CURSOR_HELP, "help", Fl_Wayland_Screen_Driver::help }, - {FL_CURSOR_MOVE, "move", Fl_Wayland_Screen_Driver::move }, - {FL_CURSOR_N, "top_side", Fl_Wayland_Screen_Driver::north }, - {FL_CURSOR_E, "right_side", Fl_Wayland_Screen_Driver::east }, - {FL_CURSOR_W, "left_side", Fl_Wayland_Screen_Driver::west }, - {FL_CURSOR_S, "bottom_side", Fl_Wayland_Screen_Driver::south }, - {FL_CURSOR_NS, "sb_v_double_arrow", Fl_Wayland_Screen_Driver::north_south }, - {FL_CURSOR_WE, "sb_h_double_arrow", Fl_Wayland_Screen_Driver::west_east }, - {FL_CURSOR_SW, "bottom_left_corner", Fl_Wayland_Screen_Driver::south_west }, - {FL_CURSOR_SE, "bottom_right_corner", Fl_Wayland_Screen_Driver::south_east }, - {FL_CURSOR_NE, "top_right_corner", Fl_Wayland_Screen_Driver::north_east }, - {FL_CURSOR_NW, "top_left_corner", Fl_Wayland_Screen_Driver::north_west }, - {FL_CURSOR_NESW, "fd_double_arrow", Fl_Wayland_Screen_Driver::nesw }, - {FL_CURSOR_NWSE, "bd_double_arrow", Fl_Wayland_Screen_Driver::nwse } - }; - - int found = -1; - for (unsigned i = 0; i < sizeof(cursor_file_array) / sizeof(struct cursor_file_struct); i++) { - if (cursor_file_array[i].c == c) { - found = cursor_file_array[i].wld_c; - if (!scr_driver->xc_cursor[found]) scr_driver->xc_cursor[found] = - scr_driver->cache_cursor(cursor_file_array[i].fname); - if (scr_driver->xc_cursor[found]) { - scr_driver->default_cursor(scr_driver->xc_cursor[found]); - } - break; - } - } - if (found < 0 || !scr_driver->xc_cursor[found]) return 0; - - if (xid->custom_cursor) { - delete_cursor(xid->custom_cursor); - xid->custom_cursor = NULL; - } - standard_cursor_ = c; - scr_driver->set_cursor(); - return 1; -} - - -void Fl_Wayland_Window_Driver::use_border() { - if (!shown() || pWindow->parent()) return; - pWindow->wait_for_expose(); // useful for border(0) just after show() - struct libdecor_frame *frame = fl_wl_xid(pWindow)->frame; - if (frame && Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::KWIN) { - if (fl_wl_xid(pWindow)->kind == DECORATED) { - libdecor_frame_set_visibility(frame, pWindow->border()); - } else { - pWindow->hide(); - pWindow->show(); - } - pWindow->redraw(); - } else { - Fl_Window_Driver::use_border(); - } -} - - -/* Change an existing window to fullscreen */ -void Fl_Wayland_Window_Driver::fullscreen_on() { - int top, bottom, left, right; - - top = fullscreen_screen_top(); - bottom = fullscreen_screen_bottom(); - left = fullscreen_screen_left(); - right = fullscreen_screen_right(); - - if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) { - top = screen_num(); - bottom = top; - left = top; - right = top; - } - pWindow->wait_for_expose(); // make sure ->xdg_toplevel is initialized - if (xdg_toplevel()) { - xdg_toplevel_set_fullscreen(xdg_toplevel(), NULL); - pWindow->_set_fullscreen(); - Fl::handle(FL_FULLSCREEN, pWindow); - } -} - - -void Fl_Wayland_Window_Driver::fullscreen_off(int X, int Y, int W, int H) { - pWindow->hide(); - pWindow->_clear_fullscreen(); - // avoid being called with W=H=0 in suboptimal scenario of #1299 - if (!W) W = w(); - if (!H) H = h(); - pWindow->resize(X, Y, W, H); - pWindow->show(); - Fl::handle(FL_FULLSCREEN, pWindow); -} - - -void Fl_Wayland_Window_Driver::label(const char *name, const char *iname) { - if (shown() && !parent() && fl_wl_xid(pWindow)->kind == DECORATED) { - if (!name) name = ""; - if (!iname) iname = fl_filename_name(name); - libdecor_frame_set_title(fl_wl_xid(pWindow)->frame, name); - } -} - - -int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty) { - int retval = set_cursor_4args(rgb, hotx, hoty, true); - if (retval) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); - Fl_Wayland_Screen_Driver::do_set_cursor(scr_driver->seat, xid->custom_cursor->wl_cursor); - } - return retval; -} - - -int Fl_Wayland_Window_Driver::set_cursor_4args(const Fl_RGB_Image *rgb, int hotx, int hoty, - bool keep_copy) { - if (keep_copy) { - if (rgb->as_svg_image()) { - int scale = wld_scale(); - Fl_RGB_Image *svg = (Fl_RGB_Image*)rgb->copy(rgb->w() * scale, rgb->h() * scale); - svg->normalize(); - svg->scale(rgb->w(), rgb->h(), 0, 1); - rgb = svg; - } else { - int ld = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d(); - uchar *data = new uchar[ld * rgb->data_h()]; - memcpy(data, rgb->array, ld * rgb->data_h()); - Fl_RGB_Image *rgb2 = new Fl_RGB_Image(data, rgb->data_w(), rgb->data_h(), rgb->d(), rgb->ld()); - rgb2->alloc_array = 1; - rgb2->scale(rgb->w(), rgb->h(), 0, 1); - rgb = rgb2; - } - } -// build a new wl_cursor and its image - struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); - struct wl_cursor *new_cursor = (struct wl_cursor*)malloc(sizeof(struct wl_cursor)); - struct cursor_image *new_image = (struct cursor_image*)calloc(1, - sizeof(struct cursor_image)); - int scale = wld_scale(); - new_image->image.width = rgb->w() * scale; - new_image->image.height = rgb->h() * scale; - new_image->image.hotspot_x = hotx * scale; - new_image->image.hotspot_y = hoty * scale; - new_image->image.delay = 0; - new_image->offset = 0; - //create a Wayland buffer and have it used as an image of the new cursor - struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen; - Fl_Image_Surface *img_surf = Fl_Wayland_Graphics_Driver::custom_offscreen( - new_image->image.width, new_image->image.height, &offscreen); - new_image->buffer = offscreen->wl_buffer; - wl_buffer_set_user_data(new_image->buffer, offscreen); - new_cursor->image_count = 1; - new_cursor->images = (struct wl_cursor_image**)malloc(sizeof(struct wl_cursor_image*)); - new_cursor->images[0] = (struct wl_cursor_image*)new_image; - new_cursor->name = strdup("custom cursor"); - // draw the rgb image to the cursor's drawing buffer - Fl_Surface_Device::push_current(img_surf); - Fl_Wayland_Graphics_Driver *driver = (Fl_Wayland_Graphics_Driver*)img_surf->driver(); - cairo_scale(driver->cr(), scale, scale); - ((Fl_RGB_Image*)rgb)->draw(0, 0); - Fl_Surface_Device::pop_current(); - delete img_surf; - memcpy(offscreen->data, offscreen->draw_buffer.buffer, offscreen->draw_buffer.data_size); - // delete the previous custom cursor, if there was one, - // and keep its Fl_RGB_Image if appropriate - if (xid->custom_cursor) delete_cursor(xid->custom_cursor, keep_copy); - //have this new cursor used - xid->custom_cursor = new custom_cursor; - xid->custom_cursor->wl_cursor = new_cursor; - xid->custom_cursor->rgb = rgb; - xid->custom_cursor->hotx = hotx; - xid->custom_cursor->hoty = hoty; - return 1; -} - - -void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) { - static int depth = 0; - struct wld_window *fl_win = fl_wl_xid(pWindow); - if (fl_win && fl_win->kind == DECORATED && !xdg_toplevel()) { - pWindow->wait_for_expose(); - } - int is_a_move = (X != x() || Y != y()); - bool true_rescale = Fl_Window::is_a_rescale(); - float f = fl_win ? Fl::screen_scale(pWindow->screen_num()) : 1; - if (fl_win && fl_win->buffer) { - int scale = wld_scale(); - int stride = cairo_format_stride_for_width( - Fl_Cairo_Graphics_Driver::cairo_format, int(W * f) * scale ); - size_t bsize = stride * int(H * f) * scale; - true_rescale = (bsize != fl_win->buffer->draw_buffer.data_size); - } - int is_a_resize = (W != w() || H != h() || true_rescale); - if (is_a_move) force_position(1); - else if (!is_a_resize && !is_a_move) return; - depth++; - if (shown() && !(parent() || popup_window())) { - X = Y = 0; - } - Fl_Window *parent = this->parent() ? pWindow->window() : NULL; - struct wld_window *parent_xid = parent ? fl_wl_xid(parent) : NULL; -//printf("resize[%p] %dx%d is_a_resize=%d is_a_move=%d depth=%d parent_xid->frame_cb=%p\n", pWindow,W,H,is_a_resize,is_a_move,depth, (parent_xid?parent_xid->frame_cb:0) ); - if (depth == 1 && fl_win && parent_xid && parent_xid->frame_cb && can_expand_outside_parent_) { - // When moving or resizing a subwindow independently from its parent while the parent window - // is being redrawn, the processing depends on whether the moved/resize window - // is a draggable-subwindow. For a draggable subwindow having can_expand_outside_parent_ != 0, - // skip the X,Y,W,H tuple to process only tuples received when parent window is ready. - // This smoothes the movement of the draggable subwindow. - // Process regular subwindows normally. - depth--; - return; - } - if (is_a_resize) { - if (pWindow->parent()) { - if (W < 1) W = 1; - if (H < 1) H = 1; - } - pWindow->Fl_Group::resize(X,Y,W,H); - //fprintf(stderr, "resize: win=%p to %dx%d\n", pWindow, W, H); - if (shown()) {pWindow->redraw();} - } else { - x(X); y(Y); - //fprintf(stderr, "move win=%p to %dx%d\n", pWindow, X, Y); - } - if (!fl_win) { - depth--; - return; - } - - if (is_a_resize) { - if (pWindow->as_overlay_window() && other_xid) { - destroy_double_buffer(); - } - if (fl_win->kind == DECORATED) { // a decorated window - if (fl_win->buffer) { - Fl_Wayland_Graphics_Driver::buffer_release(fl_win); - } - fl_win->configured_width = W; - fl_win->configured_height = H; - if (!in_handle_configure && xdg_toplevel()) { - if (Fl_Window::is_a_rescale()) size_range(); - struct libdecor_state *state = libdecor_state_new(int(W * f), int(H * f)); - // necessary only if resize is initiated by prog - libdecor_frame_commit(fl_win->frame, state, NULL); - libdecor_state_free(state); - if (libdecor_frame_is_floating(fl_win->frame)) { - fl_win->floating_width = int(W*f); - fl_win->floating_height = int(H*f); - } - } - } else if (fl_win->kind == SUBWINDOW && fl_win->subsurface) { // a subwindow - wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f); - if (!pWindow->as_gl_window()) Fl_Wayland_Graphics_Driver::buffer_release(fl_win); - fl_win->configured_width = W; - fl_win->configured_height = H; - } else if (fl_win->xdg_surface) { // a window without border - if (!pWindow->as_gl_window()) Fl_Wayland_Graphics_Driver::buffer_release(fl_win); - fl_win->configured_width = W; - fl_win->configured_height = H; - W *= f; H *= f; - xdg_surface_set_window_geometry(fl_win->xdg_surface, 0, 0, W, H); - //printf("xdg_surface_set_window_geometry: %dx%d\n",W, H); - } - } else if (!in_handle_configure && xdg_toplevel() && Fl::e_state == FL_BUTTON1) { - // Wayland doesn't provide a way for the app to set the window position on screen. - // This is functional when the move is mouse-driven. - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - xdg_toplevel_move(xdg_toplevel(), scr_driver->seat->wl_seat, scr_driver->seat->serial); - Fl::pushed(NULL); - Fl::e_state = 0; - } - - if (parent_xid) { - if (depth > 1) { - if (fl_win->subsurface) { - wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f); - wl_surface_commit(parent_xid->wl_surface); - } - } else if (parent_xid->buffer && is_a_move) { - if (fl_win->subsurface) wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f); - if (!parent_xid->buffer->wl_buffer || parent_xid->buffer->draw_buffer_needs_commit) { - if (!parent_xid->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(parent_xid); - else wl_surface_commit(parent_xid->wl_surface); - } else { - if (!parent_xid->frame_cb) { - // Use the frame callback mechanism applied to the object's parent window - parent_xid->frame_cb = wl_surface_frame(parent_xid->wl_surface); - wl_callback_add_listener(parent_xid->frame_cb, - Fl_Wayland_Graphics_Driver::p_surface_frame_listener, parent_xid); - } - wl_surface_commit(parent_xid->wl_surface); - } - } - checkSubwindowFrame(); // make sure subwindow doesn't leak outside parent - } - depth--; -} - - -static void crect_intersect(cairo_rectangle_int_t *to, cairo_rectangle_int_t *with) { - int x = fl_max(to->x, with->x); - to->width = fl_min(to->x + to->width, with->x + with->width) - x; - if (to->width < 0) to->width = 0; - int y = fl_max(to->y, with->y); - to->height = fl_min(to->y + to->height, with->y + with->height) - y; - if (to->height < 0) to->height = 0; - to->x = x; - to->y = y; -} - - -static bool crect_equal(cairo_rectangle_int_t *to, cairo_rectangle_int_t *with) { - return (to->x == with->x && to->y == with->y && to->width == with->width && - to->height == with->height); -} - - -void Fl_Wayland_Window_Driver::checkSubwindowFrame() { - if (!pWindow->parent() || can_expand_outside_parent_) return; - // make sure this subwindow doesn't leak out of its parent window - Fl_Window *from = pWindow, *parent; - cairo_rectangle_int_t full = {0, 0, pWindow->w(), pWindow->h()}; // full subwindow area - cairo_rectangle_int_t srect = full; // will become new subwindow clip - int fromx = 0, fromy = 0; - while ((parent = from->window()) != NULL) { // loop over all parent windows - fromx -= from->x(); // parent origin in subwindow's coordinates - fromy -= from->y(); - cairo_rectangle_int_t prect = {fromx, fromy, parent->w(), parent->h()}; - crect_intersect(&srect, &prect); // area of subwindow inside its parent - from = parent; - } - cairo_rectangle_int_t *r = subRect(); - // current subwindow clip - cairo_rectangle_int_t current_clip = (r ? *r : full); - if (!crect_equal(&srect, ¤t_clip)) { // if new clip differs from current clip - if (crect_equal(&srect, &full)) r = NULL; - else { - r = &srect; - if (r->width == 0 || r->height == 0) { - r = NULL; - } - } - subRect(r); - } -} - - -void Fl_Wayland_Window_Driver::subRect(cairo_rectangle_int_t *r) { - if (subRect_) delete subRect_; - cairo_rectangle_int_t *r2 = NULL; - if (r) { - r2 = new cairo_rectangle_int_t; - *r2 = *r; - } - subRect_ = r2; -} - - -void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) { - if (y == pWindow->y()) return; - // The top of the tall popup window was positioned at the top of the screen - // Instead of sliding up the popup window on the display, we slide up the - // drawing inside the fixed popup via member variable offset_y of the - // menuwindow class, and we redraw the popup content. - // It's also useful to make such tall popup window transparent. - *Fl_Window_Driver::menu_offset_y(pWindow) += (y - pWindow->y()); - struct wld_window *xid = fl_wl_xid(pWindow); - wl_surface_set_opaque_region(xid->wl_surface, NULL); - if (xid->buffer) memset(xid->buffer->draw_buffer.buffer, 0, - xid->buffer->draw_buffer.data_size); - //printf("offset_y=%d\n", *Fl_Window_Driver::menu_offset_y(pWindow)); - this->y(y); - pWindow->redraw(); -} - - -void Fl_Wayland_Window_Driver::menu_window_area(int &X, int &Y, int &W, int &H, int nscreen) { - int HH; - Fl_Window *parent = Fl_Window_Driver::menu_parent(&HH); - if (parent) { - if (pWindow->menu_window() && popup_window() && pWindow->h() > HH) { - // tall menu: set top (Y) and bottom (Y+H) bounds relatively to reference window - int ih = Fl_Window_Driver::menu_itemheight(pWindow); - X = -50000; - W = 1000000; - H = HH - 2 * ih; - Fl_Window *origin = Fl_Window_Driver::menu_leftorigin(pWindow); - if (origin) { // has left parent - int selected = fl_max(Fl_Window_Driver::menu_selected(origin), 0); - Y = origin->y() + (selected + 0.5) * ih; - } else if (!Fl_Window_Driver::menu_bartitle(pWindow)) { // tall menu button - static int y_offset = 0; - if (new_popup) { - y_offset = pWindow->y()- ih; - new_popup = false; - } - Y = 1.5 * ih + y_offset; - } else { // has a menutitle - Y = 1.5 * ih; - } - } else { // position the menu window by wayland constraints - X = -50000; - Y = -50000; - W = 1000000; - H = 1000000; - } - //printf("menu_window_area: %dx%d - %dx%d\n",X,Y,W,H); - } else Fl_Window_Driver::menu_window_area(X, Y, W, H, nscreen); -} - - -int Fl_Wayland_Window_Driver::wld_scale() { - Fl_X *flx = Fl_X::flx(pWindow); - struct wld_window *xid = (flx ? (struct wld_window *)flx->xid : NULL); - if (!xid || wl_list_empty(&xid->outputs)) { - int scale = 1; - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - Fl_Wayland_Screen_Driver::output *output; - wl_list_for_each(output, &(scr_driver->outputs), link) { - scale = fl_max(scale, output->wld_scale); - } - return scale; - } - struct surface_output *s_output; - s_output = wl_container_of(xid->outputs.next, s_output, link); - return s_output->output->wld_scale; -} - - -FL_EXPORT struct wl_surface *fl_wl_surface(struct wld_window *xid) { - return xid->wl_surface; -} - - -cairo_t *fl_wl_gc() { - return ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->cr(); -} - - -Fl_Window *fl_wl_find(struct wld_window *xid) { - return Fl_Window_Driver::find((fl_uintptr_t)xid); -} - - -struct wld_window *fl_wl_xid(const Fl_Window *win) { - return (struct wld_window *)Fl_Window_Driver::xid(win); -} - - -struct wl_compositor *fl_wl_compositor() { - Fl_Wayland_Screen_Driver *screen_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - return screen_driver->wl_compositor; -} - - -int fl_wl_buffer_scale(Fl_Window *window) { - return Fl_Wayland_Window_Driver::driver(window)->wld_scale(); -} - - -Fl_Wayland_Plugin *Fl_Wayland_Window_Driver::gl_plugin() { - static Fl_Wayland_Plugin *plugin = NULL; - if (!plugin) { - Fl_Plugin_Manager pm("wayland.fltk.org"); - plugin = (Fl_Wayland_Plugin*)pm.plugin("gl.wayland.fltk.org"); - } - return plugin; -} - - -void Fl_Wayland_Window_Driver::maximize() { - struct wld_window *xid = (struct wld_window *)Fl_X::flx(pWindow)->xid; - if (xid->kind == DECORATED) libdecor_frame_set_maximized(xid->frame); - else Fl_Window_Driver::maximize(); -} - - -void Fl_Wayland_Window_Driver::un_maximize() { - struct wld_window *xid = (struct wld_window *)Fl_X::flx(pWindow)->xid; - if (xid->kind == DECORATED) libdecor_frame_unset_maximized(xid->frame); - else Fl_Window_Driver::un_maximize(); -} diff --git a/src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx b/src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx deleted file mode 100644 index 12c525c46..000000000 --- a/src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx +++ /dev/null @@ -1,741 +0,0 @@ -// -// Wayland-specific code for clipboard and drag-n-drop support. -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#if !defined(FL_DOXYGEN) - -# include -# include -# include -# include -# include -# include "Fl_Wayland_Screen_Driver.H" -# include "Fl_Wayland_Window_Driver.H" -# include "../Unix/Fl_Unix_System_Driver.H" -# include "Fl_Wayland_Graphics_Driver.H" -# include "../../flstring.h" // includes - -# include -# include -# include -# include - - -//////////////////////////////////////////////////////////////// -// Code used for copy and paste and DnD into the program: - -static char *fl_selection_buffer[2]; -static int fl_selection_length[2]; -static const char * fl_selection_type[2]; -static int fl_selection_buffer_length[2]; -static char fl_i_own_selection[2] = {0,0}; -static struct wl_data_offer *fl_selection_offer = NULL; -// The MIME type Wayland uses for text-containing clipboard: -static const char wld_plain_text_clipboard[] = "text/plain;charset=utf-8"; - - -int Fl_Wayland_Screen_Driver::clipboard_contains(const char *type) -{ - return fl_selection_type[1] == type; -} - - -struct data_source_write_struct { - size_t rest; - char *from; -}; - -void write_data_source_cb(FL_SOCKET fd, data_source_write_struct *data) { - while (data->rest) { - ssize_t n = write(fd, data->from, data->rest); - if (n == -1) { - if (errno == EAGAIN) return; - Fl::error("write_data_source_cb: error while writing clipboard data\n"); - break; - } - data->from += n; - data->rest -= n; - } - Fl::remove_fd(fd, FL_WRITE); - delete data; - close(fd); -} - - -static void data_source_handle_send(void *data, struct wl_data_source *source, - const char *mime_type, int fd) { - fl_intptr_t rank = (fl_intptr_t)data; -//fprintf(stderr, "data_source_handle_send: %s fd=%d l=%d\n", mime_type, fd, fl_selection_length[1]); - if (((!strcmp(mime_type, wld_plain_text_clipboard) || !strcmp(mime_type, "text/plain")) && - fl_selection_type[rank] == Fl::clipboard_plain_text) - || - (!strcmp(mime_type, "image/bmp") && fl_selection_type[rank] == Fl::clipboard_image) ) { - data_source_write_struct *write_data = new data_source_write_struct; - write_data->rest = fl_selection_length[rank]; - write_data->from = fl_selection_buffer[rank]; - Fl::add_fd(fd, FL_WRITE, (Fl_FD_Handler)write_data_source_cb, write_data); - } else { - //Fl::error("Destination client requested unsupported MIME type: %s\n", mime_type); - close(fd); - } -} - - -static Fl_Window *fl_dnd_target_window = 0; -static wl_surface *fl_dnd_target_surface = 0; -static bool doing_dnd = false; // true when DnD is in action -static wl_surface *dnd_icon = NULL; // non null when DnD uses text as cursor -static wl_cursor* save_cursor = NULL; // non null when DnD uses "dnd-copy" cursor - - -static void data_source_handle_cancelled(void *data, struct wl_data_source *source) { - // An application has replaced the clipboard contents or DnD finished - wl_data_source_destroy(source); - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - if (scr_driver->seat->data_source == source) scr_driver->seat->data_source = NULL; - doing_dnd = false; - if (dnd_icon) { - struct Fl_Wayland_Graphics_Driver::wld_buffer *off = - (struct Fl_Wayland_Graphics_Driver::wld_buffer *) - wl_surface_get_user_data(dnd_icon); - struct wld_window fake_window; - memset(&fake_window, 0, sizeof(fake_window)); - fake_window.buffer = off; - Fl_Wayland_Graphics_Driver::buffer_release(&fake_window); - wl_surface_destroy(dnd_icon); - dnd_icon = NULL; - } - fl_i_own_selection[1] = 0; - if (data == 0) { // at end of DnD - if (save_cursor) { - scr_driver->default_cursor(save_cursor); - scr_driver->set_cursor(); - save_cursor = NULL; - } - if (fl_dnd_target_window) { - Fl::handle(FL_RELEASE, fl_dnd_target_window); - fl_dnd_target_window = 0; - } - Fl::pushed(0); - } -} - - -static void data_source_handle_target(void *data, struct wl_data_source *source, const char *mime_type) { - if (!Fl::pushed()) { - data_source_handle_cancelled(data, source); - return; - } - if (mime_type != NULL) { - //printf("Destination would accept MIME type if dropped: %s\n", mime_type); - } else { - //printf("Destination would reject if dropped\n"); - } -} - - -static uint32_t last_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - - -static void data_source_handle_action(void *data, struct wl_data_source *source, - uint32_t dnd_action) { - last_dnd_action = dnd_action; - switch (dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - //printf("Destination would perform a copy action if dropped\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - //printf("Destination would reject the drag if dropped\n"); - break; - } -} - - -static void data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *source) { - //printf("Drop performed\n"); -} - - -static void data_source_handle_dnd_finished(void *data, struct wl_data_source *source) { - switch (last_dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: - //printf("Destination has accepted the drop with a move action\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - //printf("Destination has accepted the drop with a copy action\n"); - break; - } -} - - -static const struct wl_data_source_listener data_source_listener = { - .target = data_source_handle_target, - .send = data_source_handle_send, - .cancelled = data_source_handle_cancelled, - .dnd_drop_performed = data_source_handle_dnd_drop_performed, - .dnd_finished = data_source_handle_dnd_finished, - .action = data_source_handle_action, -}; - - -static struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen_from_text(const char *text, - int scale) { - const char *p, *q; - int width = 0, height, w2, ltext = strlen(text); - fl_font(FL_HELVETICA, 10 * scale); - p = text; - int nl = 0; - while(nl < 20 && (q=strchr(p, '\n')) != NULL) { - nl++; - w2 = int(fl_width(p, q - p)); - if (w2 > width) width = w2; - p = q + 1; - } - if (nl < 20 && text[ ltext - 1] != '\n') { - nl++; - w2 = int(fl_width(p)); - if (w2 > width) width = w2; - } - if (width > 300*scale) width = 300*scale; - height = nl * fl_height() + 3; - width += 6; - width = ceil(width/float(scale)) * scale; // these must be multiples of scale - height = ceil(height/float(scale)) * scale; - struct Fl_Wayland_Graphics_Driver::wld_buffer *off; - Fl_Image_Surface *surf = Fl_Wayland_Graphics_Driver::custom_offscreen( - width, height, &off); - Fl_Surface_Device::push_current(surf); - p = text; - fl_font(FL_HELVETICA, 10 * scale); - int y = fl_height(); - while (nl > 0) { - q = strchr(p, '\n'); - if (q) { - fl_draw(p, q - p, 3, y); - } else { - fl_draw(p, 3, y); - break; - } - y += fl_height(); - p = q + 1; - nl--; - } - Fl_Surface_Device::pop_current(); - delete surf; - cairo_surface_flush( cairo_get_target(off->draw_buffer.cairo_) ); - memcpy(off->data, off->draw_buffer.buffer, off->draw_buffer.data_size); - return off; -} - - -int Fl_Wayland_Screen_Driver::dnd(int use_selection) { - Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); - - struct wl_data_source *source = - wl_data_device_manager_create_data_source(scr_driver->seat->data_device_manager); - // we transmit the adequate value of index in fl_selection_buffer[index] - wl_data_source_add_listener(source, &data_source_listener, (void*)0); - wl_data_source_offer(source, wld_plain_text_clipboard); - wl_data_source_set_actions(source, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); - struct Fl_Wayland_Graphics_Driver::wld_buffer *off = NULL; - int s = 1; - if (use_selection) { - // use the text as dragging icon - Fl_Widget *current = Fl::pushed() ? Fl::pushed() : Fl::first_window(); - s = Fl_Wayland_Window_Driver::driver(current->top_window())->wld_scale(); - off = (struct Fl_Wayland_Graphics_Driver::wld_buffer *)offscreen_from_text(fl_selection_buffer[0], s); - dnd_icon = wl_compositor_create_surface(scr_driver->wl_compositor); - } else dnd_icon = NULL; - doing_dnd = true; - wl_data_device_start_drag(scr_driver->seat->data_device, source, - scr_driver->seat->pointer_focus, dnd_icon, - scr_driver->seat->serial); - if (use_selection) { - wl_surface_attach(dnd_icon, off->wl_buffer, 0, 0); - wl_surface_set_buffer_scale(dnd_icon, s); - wl_surface_damage(dnd_icon, 0, 0, 10000, 10000); - wl_surface_commit(dnd_icon); - wl_surface_set_user_data(dnd_icon, off); - } else { - static struct wl_cursor *dnd_cursor = scr_driver->cache_cursor("dnd-copy"); - if (dnd_cursor) { - save_cursor = scr_driver->default_cursor(); - scr_driver->default_cursor(dnd_cursor); - scr_driver->set_cursor(); - } else save_cursor = NULL; - } - return 1; -} - - -struct compare_utf8 { // used as key_comp member of following map object - bool operator()(const char *a, const char *b) const { return strcmp(a, b) < 0; } -}; - -// map: for each clipboard mime-type FLTK has interest in, give FLTK clipboard type and priority. -// A mime-type with higher priority for same FLTK clipboard type is preferred. -typedef struct { const char * const fltk_type; int priority; } type_prio_struct; -static std::map clipboard_mimetypes_map { -// mime-type FLTK-clipboard-type priority - {"image/png", {Fl::clipboard_image, 1} }, - {"image/bmp", {Fl::clipboard_image, 2} }, - {"text/plain", {Fl::clipboard_plain_text, 1} }, - {"text/uri-list", {Fl::clipboard_plain_text, 2} }, - {"UTF8_STRING", {Fl::clipboard_plain_text, 3} }, - {wld_plain_text_clipboard, {Fl::clipboard_plain_text, 4} }, -}; - -// map: for each FLTK-clipboard-type, give current preferred mime-type and priority -typedef struct { const char *mime_type; int priority; } mime_prio_struct; -static std::map clipboard_kinds_map { -// FLTK-clipboard-type current mime-type current highest priority - {Fl::clipboard_image, {NULL, 0} }, - {Fl::clipboard_plain_text, {NULL, 0} }, -}; - - -static void data_offer_handle_offer(void *data, struct wl_data_offer *offer, - const char *mime_type) { - // runs when app becomes active once for each offered clipboard type -//fprintf(stderr, "Clipboard offer=%p supports MIME type: %s\n", offer, mime_type); - std::map::iterator iter_mime = - clipboard_mimetypes_map.find(mime_type); - if (iter_mime == clipboard_mimetypes_map.end()) return; // FLTK doesn't handle this mime_type - std::map::iterator iter_kind = - clipboard_kinds_map.find(iter_mime->second.fltk_type); - if (iter_mime->second.priority > iter_kind->second.priority) { // found mime-type with higher priority - iter_kind->second.priority = iter_mime->second.priority; - iter_kind->second.mime_type = iter_mime->first; - fl_selection_type[1] = iter_kind->first; -//fprintf(stderr,"mime_type=%s priority=%d [%s]\n",iter_kind->second.mime_type, iter_kind->second.priority, fl_selection_type[1]); - } -} - - -static void data_offer_handle_source_actions(void *data, struct wl_data_offer *offer, - uint32_t actions) { - if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { - //printf("Drag supports the copy action\n"); - } -} - - -static void data_offer_handle_action(void *data, struct wl_data_offer *offer, - uint32_t dnd_action) { - switch (dnd_action) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: - //printf("A move action would be performed if dropped\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - //printf("A copy action would be performed if dropped\n"); - break; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - //printf("The drag would be rejected if dropped\n"); - break; - } -} - - -static const struct wl_data_offer_listener data_offer_listener = { - .offer = data_offer_handle_offer, - .source_actions = data_offer_handle_source_actions, - .action = data_offer_handle_action, -}; - - -static void data_device_handle_data_offer(void *data, struct wl_data_device *data_device, - struct wl_data_offer *offer) { - // An application has created a new data source -//fprintf(stderr, "data_device_handle_data_offer offer=%p\n", offer); - fl_selection_type[1] = NULL; - wl_data_offer_add_listener(offer, &data_offer_listener, NULL); - // reset current best mime-type and priority - std::map::iterator iter = clipboard_kinds_map.begin(); - while (iter != clipboard_kinds_map.end()) { - iter->second.mime_type = NULL; - iter->second.priority = 0; - iter++; - } -} - - -static void data_device_handle_selection(void *data, struct wl_data_device *data_device, - struct wl_data_offer *offer) { - // An application has set the clipboard contents. W -//fprintf(stderr, "data_device_handle_selection\n"); - if (fl_selection_offer) wl_data_offer_destroy(fl_selection_offer); - fl_selection_offer = offer; -//if (offer == NULL) fprintf(stderr, "Clipboard is empty\n"); -} - - -// Gets from the system the clipboard or dnd text and puts it in fl_selection_buffer[1] -// which is enlarged if necessary. -static void get_clipboard_or_dragged_text(struct wl_data_offer *offer) { - int fds[2]; - char *from; - if (pipe(fds)) return; - // preferred mime-type for the text clipboard type - const char *type = clipboard_kinds_map[Fl::clipboard_plain_text].mime_type; - wl_data_offer_receive(offer, type, fds[1]); - close(fds[1]); - wl_display_flush(Fl_Wayland_Screen_Driver::wl_display); - // read in fl_selection_buffer - char *to = fl_selection_buffer[1]; - ssize_t rest = fl_selection_buffer_length[1]; - while (rest) { - ssize_t n = read(fds[0], to, rest); - if (n <= 0) { - close(fds[0]); - fl_selection_length[1] = to - fl_selection_buffer[1]; - fl_selection_buffer[1][ fl_selection_length[1] ] = 0; - goto way_out; - } - n = Fl_Screen_Driver::convert_crlf(to, n); - to += n; - rest -= n; - } - // compute size of unread clipboard data - rest = fl_selection_buffer_length[1]; - while (true) { - char buf[1000]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) { - close(fds[0]); - break; - } - rest += n; - } -//fprintf(stderr, "get_clipboard_or_dragged_text: size=%ld\n", rest); - // read full clipboard data - if (pipe(fds)) goto way_out; - wl_data_offer_receive(offer, type, fds[1]); - close(fds[1]); - wl_display_flush(Fl_Wayland_Screen_Driver::wl_display); - if (rest+1 > fl_selection_buffer_length[1]) { - delete[] fl_selection_buffer[1]; - fl_selection_buffer[1] = new char[rest+1000+1]; - fl_selection_buffer_length[1] = rest+1000; - } - from = fl_selection_buffer[1]; - while (rest > 0) { - ssize_t n = read(fds[0], from, rest); - if (n <= 0) break; - n = Fl_Screen_Driver::convert_crlf(from, n); - from += n; - rest -= n; - } - close(fds[0]); - fl_selection_length[1] = from - fl_selection_buffer[1]; - fl_selection_buffer[1][fl_selection_length[1]] = 0; -way_out: - if (strcmp(type, "text/uri-list") == 0) { - fl_decode_uri(fl_selection_buffer[1]); // decode encoded bytes - char *p = fl_selection_buffer[1]; - while (*p) { // remove prefixes - if (strncmp(p, "file://", 7) == 0) { - memmove(p, p+7, strlen(p+7)+1); - } - p = strchr(p, '\n'); - if (!p) break; - if (*++p == 0) *(p-1) = 0; // remove last '\n' - } - fl_selection_length[1] = strlen(fl_selection_buffer[1]); - } - Fl::e_clipboard_type = Fl::clipboard_plain_text; -} - - -static struct wl_data_offer *current_drag_offer = NULL; -static uint32_t fl_dnd_serial; - - -static void data_device_handle_enter(void *data, struct wl_data_device *data_device, - uint32_t serial, struct wl_surface *surface, - wl_fixed_t x, wl_fixed_t y, - struct wl_data_offer *offer) { - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface); -//printf("Drag entered our surface %p(win=%p) at %dx%d\n", surface, win, wl_fixed_to_int(x), wl_fixed_to_int(y)); - if (win) { - fl_dnd_target_surface = surface; - float f = Fl::screen_scale(win->screen_num()); - Fl::e_x = wl_fixed_to_int(x) / f; - Fl::e_y = wl_fixed_to_int(y) / f; - while (win->parent()) { - Fl::e_x += win->x(); - Fl::e_y += win->y(); - win = win->window(); - } - fl_dnd_target_window = win; - Fl::e_x_root = Fl::e_x + fl_dnd_target_window->x(); - Fl::e_y_root = Fl::e_y + fl_dnd_target_window->y(); - Fl::handle(FL_DND_ENTER, fl_dnd_target_window); - current_drag_offer = offer; - fl_dnd_serial = serial; - } else fl_dnd_target_window = NULL; // we enter a non-FLTK window (titlebar, shade) - uint32_t supported_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - uint32_t preferred_action = supported_actions; - wl_data_offer_set_actions(offer, supported_actions, preferred_action); -} - - -static void data_device_handle_motion(void *data, struct wl_data_device *data_device, - uint32_t time, wl_fixed_t x, wl_fixed_t y) { - if (!current_drag_offer) return; -//printf("data_device_handle_motion fl_dnd_target_window=%p\n", fl_dnd_target_window); - int ret = 0; - if (fl_dnd_target_window) { - float f = Fl::screen_scale(fl_dnd_target_window->screen_num()); - Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(fl_dnd_target_surface); - Fl::e_x = wl_fixed_to_int(x) / f; - Fl::e_y = wl_fixed_to_int(y) / f; - while (win->parent()) { - Fl::e_x += win->x(); - Fl::e_y += win->y(); - win = win->window(); - } - Fl::e_x_root = Fl::e_x + fl_dnd_target_window->x(); - Fl::e_y_root = Fl::e_y + fl_dnd_target_window->y(); - ret = Fl::handle(FL_DND_DRAG, fl_dnd_target_window); - if (Fl::belowmouse()) Fl::belowmouse()->take_focus(); - } - uint32_t supported_actions = ret && (Fl::pushed() || !doing_dnd) ? - WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY : WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; - uint32_t preferred_action = supported_actions; - wl_data_offer_set_actions(current_drag_offer, supported_actions, preferred_action); - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - if (ret && current_drag_offer) wl_data_offer_accept(current_drag_offer, fl_dnd_serial, "text/plain"); -} - - -static void data_device_handle_leave(void *data, struct wl_data_device *data_device) { - //printf("Drag left our surface\n"); - if (current_drag_offer) Fl::handle(FL_DND_LEAVE, fl_dnd_target_window); -} - - -static void data_device_handle_drop(void *data, struct wl_data_device *data_device) { - if (!current_drag_offer) return; - Fl::handle(FL_ENTER, fl_dnd_target_window); // useful to set the belowmouse widget - int ret = Fl::handle(FL_DND_RELEASE, fl_dnd_target_window); -//printf("data_device_handle_drop ret=%d doing_dnd=%d\n", ret, doing_dnd); - - if (!ret) { - wl_data_offer_destroy(current_drag_offer); - current_drag_offer = NULL; - return; - } - - if (doing_dnd) { - Fl::e_text = fl_selection_buffer[0]; - Fl::e_length = fl_selection_length[0]; - } else { - get_clipboard_or_dragged_text(current_drag_offer); - Fl::e_text = fl_selection_buffer[1]; - Fl::e_length = fl_selection_length[1]; - } - int old_event = Fl::e_number; - Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); - Fl::e_number = old_event; - - wl_data_offer_finish(current_drag_offer); - wl_data_offer_destroy(current_drag_offer); - current_drag_offer = NULL; -} - - -static const struct wl_data_device_listener data_device_listener = { - .data_offer = data_device_handle_data_offer, - .enter = data_device_handle_enter, - .leave = data_device_handle_leave, - .motion = data_device_handle_motion, - .drop = data_device_handle_drop, - .selection = data_device_handle_selection, -}; - - -const struct wl_data_device_listener *Fl_Wayland_Screen_Driver::p_data_device_listener = - &data_device_listener; - - -// Reads from the clipboard an image which can be in image/bmp or image/png MIME type. -// Returns 0 if OK, != 0 if error. -static int get_clipboard_image(struct wl_data_offer *offer) { - int fds[2]; - if (pipe(fds)) return 1; - // preferred mime-type for the image clipboard type - const char *type = clipboard_kinds_map[Fl::clipboard_image].mime_type; - wl_data_offer_receive(offer, type, fds[1]); - close(fds[1]); - wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); - if (strcmp(type, "image/png") == 0) { - char tmp_fname[21]; - Fl_Shared_Image *shared = 0; - strcpy(tmp_fname, "/tmp/clipboardXXXXXX"); - int fd = mkstemp(tmp_fname); - if (fd >= 0) { - while (true) { - char buf[10000]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) break; - n = write(fd, buf, n); - } - close(fd); - shared = Fl_Shared_Image::get(tmp_fname); - fl_unlink(tmp_fname); - } - close(fds[0]); - if (!shared) return 1; - int ld = shared->ld() ? shared->ld() : shared->w() * shared->d(); - uchar *rgb = new uchar[shared->w() * shared->h() * shared->d()]; - memcpy(rgb, shared->data()[0], ld * shared->h() ); - Fl_RGB_Image *image = new Fl_RGB_Image(rgb, shared->w(), shared->h(), shared->d(), - shared->ld()); - shared->release(); - image->alloc_array = 1; - Fl::e_clipboard_data = (void*)image; - } else { // process image/bmp - uchar buf[54]; - size_t rest = 1; - char *bmp = NULL; - ssize_t n = read(fds[0], buf, sizeof(buf)); // read size info of the BMP image - if (n == sizeof(buf)) { - int w, h; // size of the BMP image - Fl_Unix_System_Driver::read_int(buf + 18, w); - Fl_Unix_System_Driver::read_int(buf + 22, h); - // the number of bytes per row of BMP image, rounded up to multiple of 4 - int R = ((3*w+3)/4) * 4; - bmp = new char[R * h + 54]; - memcpy(bmp, buf, 54); - char *from = bmp + 54; - rest = R * h; - while (rest) { - ssize_t n = read(fds[0], from, rest); - if (n <= 0) break; - from += n; - rest -= n; - } -//fprintf(stderr, "get_clipboard_image: image/bmp %dx%d rest=%lu\n", w,h,rest); - } - close(fds[0]); - if (!rest) Fl::e_clipboard_data = Fl_Unix_System_Driver::own_bmp_to_RGB(bmp); - delete[] bmp; - if (rest) return 1; - } - Fl::e_clipboard_type = Fl::clipboard_image; - return 0; -} - - -void Fl_Wayland_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) { - if (clipboard != 1) return; - if (fl_i_own_selection[1]) { - // We already have it, do it quickly without compositor. - if (type == Fl::clipboard_plain_text && fl_selection_type[1] == type) { - Fl::e_text = fl_selection_buffer[1]; - Fl::e_length = fl_selection_length[1]; - if (!Fl::e_text) Fl::e_text = (char *)""; - } else if (type == Fl::clipboard_image && fl_selection_type[1] == type) { - Fl::e_clipboard_data = Fl_Unix_System_Driver::own_bmp_to_RGB(fl_selection_buffer[1]); - Fl::e_clipboard_type = Fl::clipboard_image; - } else return; - receiver.handle(FL_PASTE); - return; - } - // otherwise get the compositor to return it: - if (!fl_selection_offer) return; - if (type == Fl::clipboard_plain_text && clipboard_contains(Fl::clipboard_plain_text)) { - get_clipboard_or_dragged_text(fl_selection_offer); - Fl::e_text = fl_selection_buffer[1]; - Fl::e_length = fl_selection_length[1]; - receiver.handle(FL_PASTE); - } else if (type == Fl::clipboard_image && clipboard_contains(Fl::clipboard_image)) { - if (get_clipboard_image(fl_selection_offer)) return; - struct wld_window * xid = fl_wl_xid(receiver.top_window()); - if (xid) { - int s = Fl_Wayland_Window_Driver::driver(receiver.top_window())->wld_scale(); - if ( s > 1) { - Fl_RGB_Image *rgb = (Fl_RGB_Image*)Fl::e_clipboard_data; - rgb->scale(rgb->data_w() / s, rgb->data_h() / s); - } - } - int done = receiver.handle(FL_PASTE); - Fl::e_clipboard_type = ""; - if (done == 0) { - delete (Fl_RGB_Image*)Fl::e_clipboard_data; - Fl::e_clipboard_data = NULL; - } - } -} - - -void Fl_Wayland_Screen_Driver::copy(const char *stuff, int len, int clipboard, - const char *type) { - if (!stuff || len < 0) return; - - if (clipboard >= 2) - clipboard = 1; // Only on X11 do multiple clipboards make sense. - - if (len+1 > fl_selection_buffer_length[clipboard]) { - delete[] fl_selection_buffer[clipboard]; - fl_selection_buffer[clipboard] = new char[len+100]; - fl_selection_buffer_length[clipboard] = len+100; - } - memcpy(fl_selection_buffer[clipboard], stuff, len); - fl_selection_buffer[clipboard][len] = 0; // needed for direct paste - fl_selection_length[clipboard] = len; - fl_i_own_selection[clipboard] = 1; - fl_selection_type[clipboard] = Fl::clipboard_plain_text; - if (clipboard == 1) { - if (seat->data_source) wl_data_source_destroy(seat->data_source); - seat->data_source = wl_data_device_manager_create_data_source(seat->data_device_manager); - // we transmit the adequate value of index in fl_selection_buffer[index] - wl_data_source_add_listener(seat->data_source, &data_source_listener, (void*)1); - wl_data_source_offer(seat->data_source, wld_plain_text_clipboard); - wl_data_device_set_selection(seat->data_device, - seat->data_source, - seat->keyboard_enter_serial); -//fprintf(stderr, "wl_data_device_set_selection len=%d to %d\n", len, clipboard); - } -} - - -// takes a raw RGB image and puts it in the copy/paste buffer -void Fl_Wayland_Screen_Driver::copy_image(const unsigned char *data, int W, int H){ - if (!data || W <= 0 || H <= 0) return; - delete[] fl_selection_buffer[1]; - fl_selection_buffer[1] = - (char *)Fl_Unix_System_Driver::create_bmp(data,W,H,&fl_selection_length[1]); - fl_selection_buffer_length[1] = fl_selection_length[1]; - fl_i_own_selection[1] = 1; - fl_selection_type[1] = Fl::clipboard_image; - if (seat->data_source) wl_data_source_destroy(seat->data_source); - seat->data_source = wl_data_device_manager_create_data_source(seat->data_device_manager); - // we transmit the adequate value of index in fl_selection_buffer[index] - wl_data_source_add_listener(seat->data_source, &data_source_listener, (void*)1); - wl_data_source_offer(seat->data_source, "image/bmp"); - wl_data_device_set_selection(seat->data_device, seat->data_source, - seat->keyboard_enter_serial); -//fprintf(stderr, "copy_image: len=%d\n", fl_selection_length[1]); -} - -//////////////////////////////////////////////////////////////// -// Code for tracking clipboard changes: - -// is that possible with Wayland ? - -//////////////////////////////////////////////////////////////// - -#endif // !defined(FL_DOXYGEN) diff --git a/src/drivers/Wayland/fl_wayland_platform_init.cxx b/src/drivers/Wayland/fl_wayland_platform_init.cxx deleted file mode 100644 index 4c4477740..000000000 --- a/src/drivers/Wayland/fl_wayland_platform_init.cxx +++ /dev/null @@ -1,157 +0,0 @@ -// -// Wayland-specific code to initialize wayland support. -// -// Copyright 2022-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include "Fl_Wayland_Copy_Surface_Driver.H" -#include "Fl_Wayland_Graphics_Driver.H" -#include "Fl_Wayland_Screen_Driver.H" -#include "../Unix/Fl_Unix_System_Driver.H" -#include "Fl_Wayland_Window_Driver.H" -#include "Fl_Wayland_Image_Surface_Driver.H" -#include "../Base/Fl_Base_Pen_Events.H" -#ifdef FLTK_USE_X11 -# include "../Xlib/Fl_Xlib_Copy_Surface_Driver.H" -# include "../Cairo/Fl_X11_Cairo_Graphics_Driver.H" -# include "../X11/Fl_X11_Screen_Driver.H" -# include "../X11/Fl_X11_Window_Driver.H" -# include "../Xlib/Fl_Xlib_Image_Surface_Driver.H" -#endif -#include -#include -#include - - -#ifdef FLTK_USE_X11 - -static bool attempt_wayland() { - if (Fl_Wayland_Screen_Driver::wl_display) return true; - static bool first = true; - static bool disable_wl = false; - if (first) { // get the value if it exists and cache it - void *sym = Fl_Posix_System_Driver::dlopen_or_dlsym(NULL, "fl_disable_wayland"); - if (sym) { - disable_wl = *(bool *)sym; - // printf("fl_disable_wayland = %s\n", disable_wl ? "true" : "false"); - } - first = false; - } - if (disable_wl) - return false; - const char *backend = ::getenv("FLTK_BACKEND"); - // fprintf(stderr, "FLTK_BACKEND='%s'\n", backend ? backend : ""); - if (backend && strcmp(backend, "x11") == 0) { - return false; - } - - if (backend && strcmp(backend, "wayland") == 0) { - Fl_Wayland_Screen_Driver::wl_display = wl_display_connect(NULL); - if (!Fl_Wayland_Screen_Driver::wl_display) { - fprintf(stderr, "Error: no Wayland connection available, FLTK_BACKEND='wayland'\n"); - exit(1); - } - return true; - } - - if (!backend) { - // env var XDG_RUNTIME_DIR is required for Wayland - const char *xdgrt = ::getenv("XDG_RUNTIME_DIR"); - if (xdgrt) { - // is a Wayland connection available ? - Fl_Wayland_Screen_Driver::wl_display = wl_display_connect(NULL); - if (Fl_Wayland_Screen_Driver::wl_display) { // Yes, use Wayland drivers - // puts("using wayland"); - return true; - } - } - // no Wayland connection or environment variable XDG_RUNTIME_DIR not set, - // falling back to X11 - return false; - } - - fprintf(stderr, "Error: unexpected value of FLTK_BACKEND: '%s'\n", backend); - exit(1); - return false; -} - -#endif // FLTK_USE_X11 - - -Fl_System_Driver *Fl_System_Driver::newSystemDriver() { - return new Fl_Unix_System_Driver(); -} - - -Fl_Graphics_Driver *Fl_Graphics_Driver::newMainGraphicsDriver() { -#ifdef FLTK_USE_X11 - if (!attempt_wayland()) return new Fl_X11_Cairo_Graphics_Driver(); -#endif - return new Fl_Wayland_Graphics_Driver(); -} - - -Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h) { -#ifdef FLTK_USE_X11 - if (!Fl_Wayland_Screen_Driver::wl_display) return new Fl_Xlib_Copy_Surface_Driver(w, h); -#endif - return new Fl_Wayland_Copy_Surface_Driver(w, h); -} - - -Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver() { - if (!Fl_Screen_Driver::system_driver) Fl::system_driver(); -#ifdef FLTK_USE_X11 - if (attempt_wayland()) { - return new Fl_Wayland_Screen_Driver(); - } - - Fl_X11_Screen_Driver *d = new Fl_X11_Screen_Driver(); - for (int i = 0; i < MAX_SCREENS; i++) d->screens[i].scale = 1; - d->current_xft_dpi = 0.; // means the value of the Xft.dpi resource is still unknown - return d; -#else - return new Fl_Wayland_Screen_Driver(); -#endif -} - - -Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w) -{ -#ifdef FLTK_USE_X11 - if (!attempt_wayland()) return new Fl_X11_Window_Driver(w); -#endif - return new Fl_Wayland_Window_Driver(w); -} - - -Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, int h, int high_res, Fl_Offscreen off) -{ -#ifdef FLTK_USE_X11 - if (!attempt_wayland()) - return new Fl_Xlib_Image_Surface_Driver(w, h, high_res, off); -#endif - return new Fl_Wayland_Image_Surface_Driver(w, h, high_res, off); -} - -#if defined(FLTK_HAVE_PEN_SUPPORT) - -namespace Fl { -namespace Pen { -Driver default_driver; -Driver& driver = default_driver; -} // namespace Pen -} // namespace Fl - -#endif // FLTK_HAVE_PEN_SUPPORT diff --git a/src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.H deleted file mode 100644 index 2958fe6e5..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.H +++ /dev/null @@ -1,62 +0,0 @@ -// -// Class Fl_WinAPI_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#ifndef FL_WINAPI_GL_WINDOW_DRIVER_H -#define FL_WINAPI_GL_WINDOW_DRIVER_H - -#include -#if HAVE_GL -#include -#include "../../Fl_Gl_Window_Driver.H" -#include - -class Fl_WinAPI_Gl_Window_Driver : public Fl_Gl_Window_Driver { - friend Fl_Gl_Window_Driver* Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *); - Fl_WinAPI_Gl_Window_Driver(Fl_Gl_Window *win) : Fl_Gl_Window_Driver(win) {} - float pixels_per_unit() FL_OVERRIDE; - int mode_(int m, const int *a) FL_OVERRIDE; - void make_current_after() FL_OVERRIDE; - void swap_buffers() FL_OVERRIDE; - void swap_interval(int) FL_OVERRIDE; - int swap_interval() const FL_OVERRIDE; - void invalidate() FL_OVERRIDE {} - int flush_begin(char& valid_f) FL_OVERRIDE; - Fl_Gl_Choice *find(int m, const int *alistp) FL_OVERRIDE; - GLContext create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) FL_OVERRIDE; - GLContext do_create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g, - int layer); - void set_gl_context(Fl_Window* w, GLContext context) FL_OVERRIDE; - void delete_gl_context(GLContext) FL_OVERRIDE; - void make_overlay_current() FL_OVERRIDE; - void redraw_overlay() FL_OVERRIDE; - void* GetProcAddress(const char *procName) FL_OVERRIDE; - void draw_string_legacy(const char* str, int n) FL_OVERRIDE; - void gl_bitmap_font(Fl_Font_Descriptor *fl_fontsize) FL_OVERRIDE; - void get_list(Fl_Font_Descriptor *fd, int r) FL_OVERRIDE; - int genlistsize() FL_OVERRIDE; - void switch_to_GL1() FL_OVERRIDE; - void switch_back() FL_OVERRIDE; -#if HAVE_GL_OVERLAY - void gl_hide_before(void *& overlay) FL_OVERRIDE; - int can_do_overlay() FL_OVERRIDE; - int overlay_color(Fl_Color i) FL_OVERRIDE; - void make_overlay(void*&overlay) FL_OVERRIDE; -#endif -}; - -#endif // HAVE_GL - -#endif // FL_WINAPI_GL_WINDOW_DRIVER_H diff --git a/src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx deleted file mode 100644 index 8e199b09c..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Gl_Window_Driver.cxx +++ /dev/null @@ -1,472 +0,0 @@ -// -// Class Fl_WinAPI_Gl_Window_Driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2021-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#if HAVE_GL -#include -#include "../../Fl_Screen_Driver.H" -#include -#include "Fl_WinAPI_Gl_Window_Driver.H" -#include "../../Fl_Gl_Choice.H" -#include "Fl_WinAPI_Window_Driver.H" -#include "../GDI/Fl_Font.H" -extern void fl_save_dc(HWND, HDC); - -#ifndef GL_CURRENT_PROGRAM -# define GL_CURRENT_PROGRAM 0x8B8D // from glew.h -#endif - -// STR #3119: select pixel format with composition support -// ... and no more than 32 color bits (8 bits/color) -// Ref: PixelFormatDescriptor Object -// https://msdn.microsoft.com/en-us/library/cc231189.aspx -#if !defined(PFD_SUPPORT_COMPOSITION) -# define PFD_SUPPORT_COMPOSITION (0x8000) -#endif - -#define DEBUG_PFD (0) // 1 = PFD selection debug output, 0 = no debug output - - -// Describes crap needed to create a GLContext. -class Fl_WinAPI_Gl_Choice : public Fl_Gl_Choice { - friend class Fl_WinAPI_Gl_Window_Driver; -private: - int pixelformat; // the visual to use - PIXELFORMATDESCRIPTOR pfd; // some wgl calls need this thing -public: - Fl_WinAPI_Gl_Choice(int m, const int *alistp, Fl_Gl_Choice *n) : Fl_Gl_Choice(m, alistp, n) { - pixelformat = 0; - } -}; - - -Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w) -{ - return new Fl_WinAPI_Gl_Window_Driver(w); -} - - -Fl_Gl_Choice *Fl_WinAPI_Gl_Window_Driver::find(int m, const int *alistp) -{ - Fl_WinAPI_Gl_Choice *g = (Fl_WinAPI_Gl_Choice*)Fl_Gl_Window_Driver::find_begin(m, alistp); - if (g) return g; - - // Replacement for ChoosePixelFormat() that finds one with an overlay if possible: - HDC gc = (HDC)(fl_graphics_driver ? fl_graphics_driver->gc() : 0); - if (!gc) gc = fl_GetDC(0); - int pixelformat = 0; - PIXELFORMATDESCRIPTOR chosen_pfd; - for (int i = 1; ; i++) { - PIXELFORMATDESCRIPTOR pfd; - if (!DescribePixelFormat(gc, i, sizeof(pfd), &pfd)) break; - // continue if it does not satisfy our requirements: - if (~pfd.dwFlags & (PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL)) continue; - if (pfd.iPixelType != ((m&FL_INDEX)?PFD_TYPE_COLORINDEX:PFD_TYPE_RGBA)) continue; - if ((m & FL_ALPHA) && !pfd.cAlphaBits) continue; - if ((m & FL_ACCUM) && !pfd.cAccumBits) continue; - if ((!(m & FL_DOUBLE)) != (!(pfd.dwFlags & PFD_DOUBLEBUFFER))) continue; - if ((!(m & FL_STEREO)) != (!(pfd.dwFlags & PFD_STEREO))) continue; - // Skipt his descriptor if we want a depth buffer, but this one has none - if ((m & FL_DEPTH) && !pfd.cDepthBits) continue; - // Skipt his descriptor if we want a 32 bit depth buffer, but this one has less or none - if ((m & FL_DEPTH32) && pfd.cDepthBits < 32) continue; - if ((m & FL_STENCIL) && !pfd.cStencilBits) continue; - -#if DEBUG_PFD - printf("pfd #%d supports composition: %s\n", i, (pfd.dwFlags & PFD_SUPPORT_COMPOSITION) ? "yes" : "no"); - printf(" ... & PFD_GENERIC_FORMAT: %s\n", (pfd.dwFlags & PFD_GENERIC_FORMAT) ? "generic" : "accelerated"); - printf(" ... Overlay Planes : %d\n", pfd.bReserved & 15); - printf(" ... Color & Depth : %d, %d\n", pfd.cColorBits, pfd.cDepthBits); - if (pixelformat) - printf(" current pixelformat : %d\n", pixelformat); - fflush(stdout); -#endif // DEBUG_PFD - - // see if better than the one we have already: - if (pixelformat) { - // offering non-generic rendering is better (read: hardware acceleration) - if (!(chosen_pfd.dwFlags & PFD_GENERIC_FORMAT) && - (pfd.dwFlags & PFD_GENERIC_FORMAT)) continue; - // offering overlay is better: - else if (!(chosen_pfd.bReserved & 15) && (pfd.bReserved & 15)) {} - // otherwise prefer a format that supports composition (STR #3119) - else if ((chosen_pfd.dwFlags & PFD_SUPPORT_COMPOSITION) && - !(pfd.dwFlags & PFD_SUPPORT_COMPOSITION)) continue; - // otherwise more bit planes is better, but no more than 32 (8 bits per channel): - else if (pfd.cColorBits > 32 || chosen_pfd.cColorBits > pfd.cColorBits) continue; - else if (chosen_pfd.cDepthBits > pfd.cDepthBits) continue; - } - pixelformat = i; - chosen_pfd = pfd; - } - -#if DEBUG_PFD - static int bb = 0; - if (!bb) { - bb = 1; - printf("PFD_SUPPORT_COMPOSITION = 0x%x\n", PFD_SUPPORT_COMPOSITION); - } - printf("Chosen pixel format is %d\n", pixelformat); - printf("Color bits = %d, Depth bits = %d\n", chosen_pfd.cColorBits, chosen_pfd.cDepthBits); - printf("Pixel format supports composition: %s\n", (chosen_pfd.dwFlags & PFD_SUPPORT_COMPOSITION) ? "yes" : "no"); - fflush(stdout); -#endif // DEBUG_PFD - - if (!pixelformat) return 0; - - g = new Fl_WinAPI_Gl_Choice(m, alistp, first); - first = g; - - g->pixelformat = pixelformat; - g->pfd = chosen_pfd; - - return g; -} - - -GLContext Fl_WinAPI_Gl_Window_Driver::do_create_gl_context(Fl_Window* window, - const Fl_Gl_Choice* g, int layer) -{ - Fl_X* i = Fl_X::flx(window); - HDC hdc = Fl_WinAPI_Window_Driver::driver(window)->private_dc; - if (!hdc) { - hdc = Fl_WinAPI_Window_Driver::driver(window)->private_dc = GetDCEx((HWND)i->xid, 0, DCX_CACHE); - fl_save_dc((HWND)i->xid, hdc); - SetPixelFormat(hdc, ((Fl_WinAPI_Gl_Choice*)g)->pixelformat, (PIXELFORMATDESCRIPTOR*)(&((Fl_WinAPI_Gl_Choice*)g)->pfd)); -# if USE_COLORMAP - if (fl_palette) SelectPalette(hdc, fl_palette, FALSE); -# endif - } - GLContext context = layer ? wglCreateLayerContext(hdc, layer) : wglCreateContext(hdc); - if (context) { - if (context_list && nContext) - wglShareLists((HGLRC)context_list[0], (HGLRC)context); - add_context(context); - } - return context; -} - - -GLContext Fl_WinAPI_Gl_Window_Driver::create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) -{ - return do_create_gl_context(window, g, 0); -} - -void Fl_WinAPI_Gl_Window_Driver::set_gl_context(Fl_Window* w, GLContext context) { - GLContext current_context = wglGetCurrentContext(); - if (context != current_context || w != cached_window) { - cached_window = w; - wglMakeCurrent(Fl_WinAPI_Window_Driver::driver(w)->private_dc, (HGLRC)context); - } -} - -void Fl_WinAPI_Gl_Window_Driver::delete_gl_context(GLContext context) { - GLContext current_context = wglGetCurrentContext(); - if (current_context == context) { - cached_window = 0; - wglMakeCurrent(0, 0); - } - wglDeleteContext((HGLRC)context); - del_context(context); -} - - -void Fl_WinAPI_Gl_Window_Driver::make_overlay_current() { -#if HAVE_GL_OVERLAY - if (overlay() != this) { - set_gl_context(pWindow, (GLContext)overlay()); - // if (fl_overlay_depth) - // wglRealizeLayerPalette(Fl_X::flx(this)->private_dc, 1, TRUE); - } else -#endif - glDrawBuffer(GL_FRONT); -} - -void Fl_WinAPI_Gl_Window_Driver::redraw_overlay() { - pWindow->damage(FL_DAMAGE_OVERLAY); -} - -#if HAVE_GL_OVERLAY - -// Methods on Fl_Gl_Window_driver that create an overlay window. - -// Under win32 another GLX context is created to draw into the overlay -// and it is stored in the "overlay" pointer. - -// If overlay hardware is unavailable, the overlay is -// "faked" by drawing into the main layers. This is indicated by -// setting overlay == this. - -//static COLORREF *palette; -static int fl_overlay_depth = 0; - -void Fl_WinAPI_Gl_Window_Driver::gl_hide_before(void *& overlay) { - if (overlay && overlay != pWindow) { - delete_gl_context((GLContext)overlay); - overlay = 0; - } -} - -void Fl_WinAPI_Gl_Window_Driver::make_overlay(void*&overlay) { - if (overlay) return; - - GLContext context = do_create_gl_context(pWindow, g(), 1); - if (!context) {overlay = pWindow; return;} // fake the overlay - - HDC hdc = Fl_WinAPI_Window_Driver::driver(pWindow)->private_dc; - overlay = context; - LAYERPLANEDESCRIPTOR pfd; - wglDescribeLayerPlane(hdc, g()->pixelformat, 1, sizeof(pfd), &pfd); - if (!pfd.iPixelType) { - ; // full-color overlay - } else { - fl_overlay_depth = pfd.cColorBits; // used by gl_color() - if (fl_overlay_depth > 8) fl_overlay_depth = 8; - COLORREF palette[256]; - int n = (1<valid(0); - return; -} - -int Fl_WinAPI_Gl_Window_Driver::can_do_overlay() { - if (!g()) { - g( find(mode(), alist()) ); - if (!g()) return 0; - } - return (g()->pfd.bReserved & 15) != 0; -} - -int Fl_WinAPI_Gl_Window_Driver::overlay_color(Fl_Color i) { - if (Fl_Xlib_Graphics_Driver::fl_overlay && fl_overlay_depth) { - if (fl_overlay_depth < 8) { - // only black & white produce the expected colors. This could - // be improved by fixing the colormap set in Fl_Gl_Overlay.cxx - int size = 1<= size-2) glIndexi(size-1); - else glIndexi(i); - } else { - glIndexi(i ? i : FL_GRAY_RAMP); - } - return 1; - } - return 0; -} - -#endif // HAVE_GL_OVERLAY - - -float Fl_WinAPI_Gl_Window_Driver::pixels_per_unit() -{ - int ns = Fl_Window_Driver::driver(pWindow)->screen_num(); - return Fl::screen_driver()->scale(ns); -} - - -int Fl_WinAPI_Gl_Window_Driver::mode_(int m, const int *a) { - int oldmode = mode(); - pWindow->context(0); - mode( m); alist(a); - if (pWindow->shown()) { - g( find(m, a) ); - if (!g() || (oldmode^m)&(FL_DOUBLE|FL_STEREO)) { - pWindow->hide(); - pWindow->show(); - } - } else { - g(0); - } - return 1; -} - -void Fl_WinAPI_Gl_Window_Driver::make_current_after() { -#if USE_COLORMAP - if (fl_palette) { - fl_GetDC(fl_xid(pWindow)); - SelectPalette((HDC)fl_graphics_driver->gc(), fl_palette, FALSE); - RealizePalette((HDC)fl_graphics_driver->gc()); - } -#endif // USE_COLORMAP -} - -//#define HAVE_GL_OVERLAY 1 //test only - -void Fl_WinAPI_Gl_Window_Driver::swap_buffers() { -# if HAVE_GL_OVERLAY - // Do not swap the overlay, to match GLX: - BOOL ret = wglSwapLayerBuffers(Fl_WinAPI_Window_Driver::driver(pWindow)->private_dc, WGL_SWAP_MAIN_PLANE); - DWORD err = GetLastError(); -# else - SwapBuffers(Fl_WinAPI_Window_Driver::driver(pWindow)->private_dc); -# endif -} - - -// Start of swap_interval implementation in the three possibel ways for X11 - -// -1 = not yet initialized, 0 = none found, 1 = GLX, 2 = MESA, 3 = SGI -static signed char swap_interval_type = -1; - -typedef const char *(WINAPI *WGL_Get_Extension_String_Proc)(); -typedef BOOL (WINAPI *WGL_Swap_Iterval_Proc)(int interval); -typedef int (WINAPI *WGL_Get_Swap_Iterval_Proc)(); - -static WGL_Swap_Iterval_Proc wglSwapIntervalEXT = NULL; -static WGL_Get_Swap_Iterval_Proc wglGetSwapIntervalEXT = NULL; - -static void init_swap_interval() { - if (swap_interval_type != -1) - return; - swap_interval_type = 0; - WGL_Get_Extension_String_Proc wglGetExtensionsStringEXT = NULL; - wglGetExtensionsStringEXT = (WGL_Get_Extension_String_Proc)wglGetProcAddress("wglGetExtensionsStringEXT"); - if (!wglGetExtensionsStringEXT) - return; - const char *extensions = wglGetExtensionsStringEXT(); - if (extensions && strstr(extensions, "WGL_EXT_swap_control")) { - wglSwapIntervalEXT = (WGL_Swap_Iterval_Proc)wglGetProcAddress("wglSwapIntervalEXT"); - wglGetSwapIntervalEXT = (WGL_Get_Swap_Iterval_Proc)wglGetProcAddress("wglGetSwapIntervalEXT"); - swap_interval_type = 1; - } -} - -void Fl_WinAPI_Gl_Window_Driver::swap_interval(int interval) { - if (swap_interval_type == -1) - init_swap_interval(); - if (swap_interval_type == 1) { - if (wglSwapIntervalEXT) - wglSwapIntervalEXT(interval); - } -} - -int Fl_WinAPI_Gl_Window_Driver::swap_interval() const { - if (swap_interval_type == -1) - init_swap_interval(); - int interval = -1; - if (swap_interval_type == 1) { - if (wglGetSwapIntervalEXT) - interval = wglGetSwapIntervalEXT(); - } - return interval; -} - -// end of swap_interval implementation - -#if HAVE_GL_OVERLAY -#endif - -int Fl_WinAPI_Gl_Window_Driver::flush_begin(char& valid_f_) { -#if HAVE_GL_OVERLAY - char save_valid_f = valid_f_; - // Draw into hardware overlay planes if they are damaged: - if (overlay() && overlay() != pWindow - && (pWindow->damage()&(FL_DAMAGE_OVERLAY|FL_DAMAGE_EXPOSE) || !save_valid_f & 1)) { - set_gl_context(pWindow, (GLContext)overlay()); - if (fl_overlay_depth) - wglRealizeLayerPalette(Fl_WinAPI_Window_Driver::driver(pWindow)->private_dc, 1, TRUE); - glDisable(GL_SCISSOR_TEST); - glClear(GL_COLOR_BUFFER_BIT); - Fl_Xlib_Graphics_Driver::fl_overlay = 1; - draw_overlay(); - Fl_Xlib_Graphics_Driver::fl_overlay = 0; - valid_f_ = save_valid_f; - wglSwapLayerBuffers(Fl_WinAPI_Window_Driver::driver(pWindow)->private_dc, WGL_SWAP_OVERLAY1); - // if only the overlay was damaged we are done, leave main layer alone: - if (pWindow->damage() == FL_DAMAGE_OVERLAY) { - return 1; - } - } -#endif - return 0; -} - -void* Fl_WinAPI_Gl_Window_Driver::GetProcAddress(const char *procName) { - return (void*)wglGetProcAddress((LPCSTR)procName); -} - - -void Fl_WinAPI_Gl_Window_Driver::draw_string_legacy(const char* str, int n) { - draw_string_legacy_get_list(str, n); -} - -int Fl_WinAPI_Gl_Window_Driver::genlistsize() { - return 0x10000; -} - -void Fl_WinAPI_Gl_Window_Driver::gl_bitmap_font(Fl_Font_Descriptor *fl_fontsize) { - if (!fl_fontsize->listbase) { - fl_fontsize->listbase = glGenLists(genlistsize()); - } - glListBase(fl_fontsize->listbase); -} - -void Fl_WinAPI_Gl_Window_Driver::get_list(Fl_Font_Descriptor *fd, int r) { - Fl_GDI_Font_Descriptor* gl_fd = (Fl_GDI_Font_Descriptor*)fd; - if (gl_fd->glok[r]) return; - gl_fd->glok[r] = 1; - unsigned int ii = r * 0x400; - HFONT oldFid = (HFONT)SelectObject((HDC)fl_graphics_driver->gc(), gl_fd->fid); - wglUseFontBitmapsW((HDC)fl_graphics_driver->gc(), ii, 0x400, gl_fd->listbase+ii); - SelectObject((HDC)fl_graphics_driver->gc(), oldFid); -} - - -typedef void (WINAPI *glUseProgram_type)(GLint); -static glUseProgram_type glUseProgram_f = NULL; - -void Fl_WinAPI_Gl_Window_Driver::switch_to_GL1() { - if (!glUseProgram_f) { - glUseProgram_f = (glUseProgram_type)GetProcAddress("glUseProgram"); - } - glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_prog); - if (current_prog) glUseProgram_f(0); -} - -void Fl_WinAPI_Gl_Window_Driver::switch_back() { - if (current_prog) glUseProgram_f((GLuint)current_prog); -} - - -class Fl_WinAPI_Gl_Plugin : public Fl_WinAPI_Plugin { -public: - Fl_WinAPI_Gl_Plugin() : Fl_WinAPI_Plugin(name()) { } - const char *name() override { return "gl.winapi.fltk.org"; } - void invalidate(Fl_Window *w) override { - w->as_gl_window()->valid(0); - } -}; - - -static Fl_WinAPI_Gl_Plugin Gl_Invalidate_Plugin; - - -FL_EXPORT HGLRC fl_win32_glcontext(GLContext rc) { return (HGLRC)rc; } - -#endif // HAVE_GL diff --git a/src/drivers/WinAPI/Fl_WinAPI_Pen_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Pen_Driver.cxx deleted file mode 100644 index 06cc1e477..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Pen_Driver.cxx +++ /dev/null @@ -1,518 +0,0 @@ -// -// Definition of Windows Pen/Tablet event driver. -// -// Copyright 2025-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Note: We require Windows 8 or later features for Pen/Tablet support. -// Defining WINVER and _WIN32_WINNT to 0x0602 *may* be required on some -// Windows build platforms. Must be done before all #include's. - -#if !defined(WINVER) || (WINVER < 0x0602) -# ifdef WINVER -# undef WINVER -# endif -# define WINVER 0x0602 -#endif -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0602) -# ifdef _WIN32_WINNT -# undef _WIN32_WINNT -# endif -# define _WIN32_WINNT 0x0602 -#endif - -#include "src/drivers/Base/Fl_Base_Pen_Events.H" - -#include -#include -#include -#include "../../Fl_Screen_Driver.H" -#include -#include -#include -#include -// Some versions of MinGW now require us to explicitly include winerror to get S_OK defined -#include - -extern Fl_Window *fl_xmousewin; - -static constexpr uint8_t _FL_PEN = 0; // internal use -static constexpr uint8_t _FL_ERASER = 1; // internal use -static uint8_t device_type_ = _FL_PEN; - -static int _e_x_down = 0; -static int _e_y_down = 0; - -// Click counting state -static DWORD last_click_time_ = 0; -static int last_click_x_ = 0; -static int last_click_y_ = 0; -static Fl::Pen::State last_click_trigger_ = Fl::Pen::State::NONE; - -// The trait list keeps track of traits for every pen ID that appears while -// handling events. -// AppKit does not tell us what traits are available per pen or tablet, so -// we use the first 5 motion events to discover event values that are not -// the default value, and enter that knowledge into the traits database. -typedef std::map TraitList; -static TraitList trait_list_; -static int trait_countdown_ { 5 }; -static int current_pen_id_ { -1 }; -static Fl::Pen::Trait current_pen_trait_ { Fl::Pen::Trait::DRIVER_AVAILABLE }; -static Fl::Pen::Trait driver_traits_ { - Fl::Pen::Trait::DRIVER_AVAILABLE | Fl::Pen::Trait::PEN_ID | - Fl::Pen::Trait::ERASER | Fl::Pen::Trait::PRESSURE | - Fl::Pen::Trait::TILT_X | - Fl::Pen::Trait::TILT_Y | Fl::Pen::Trait::TWIST - // Notably missing: PROXIMITY, BARREL_PRESSURE -}; - -// Temporary storage of event data for the driver; -static Fl::Pen::EventData ev; - - -namespace Fl { - -// namespace Private { - -// // Global mouse position at mouse down event -// extern int e_x_down; -// extern int e_y_down; - -// }; // namespace Private - -namespace Pen { - -class Windows_Driver : public Driver { -public: - Windows_Driver() = default; - //virtual void subscribe(Fl_Widget* widget) override; - //virtual void unsubscribe(Fl_Widget* widget) override; - //virtual void release() override; - virtual Trait traits() override { return driver_traits_; } - virtual Trait pen_traits(int pen_id) override { - auto it = trait_list_.find(pen_id); - if (pen_id == 0) - return current_pen_trait_; - if (it == trait_list_.end()) { - return Trait::DRIVER_AVAILABLE; - } else { - return it->second; - } - } -}; - -Windows_Driver windows_driver; -Driver& driver { windows_driver }; - -} // namespace Pen - -} // namespace Fl - - -using namespace Fl::Pen; - -/* - Copy the event state. - */ -static void copy_state() { - Fl::Pen::State tr = (Fl::Pen::State)((uint32_t)Fl::Pen::e.state ^ (uint32_t)ev.state); - Fl::Pen::e = ev; - Fl::Pen::e.trigger = tr; - Fl::e_x = (int)ev.x; - Fl::e_y = (int)ev.y; - Fl::e_x_root = (int)ev.rx; - Fl::e_y_root = (int)ev.ry; -} - -/* - Check if coordinates are within the widget box. - Coordinates are in top_window space. We iterate up the hierarchy to ensure - that we handle subwindows correctly. - */ -static bool event_inside(Fl_Widget *w, double x, double y) { - if (w->as_window()) { - return ((x >= 0) && (y >= 0) && (x < w->w()) && (y < w->h())); - } else { - return ((x >= w->x()) && (y >= w->y()) && (x < w->x() + w->w()) && (y < w->y() + w->h())); - } -} - -/* - Find the widget under the pen event. - Search the subscriber list for widgets that are inside the same window, - are visible, and are within the give coordinates. Subwindow aware. - */ -static Fl_Widget *find_below_pen(Fl_Window *win, double x, double y) { - for (auto &sub: subscriber_list_) { - Fl_Widget *candidate = sub.second->widget(); - if (candidate && ((candidate == win) || (!candidate->as_window() && candidate->window() == win))) { - if (candidate->visible() && event_inside(candidate, x, y)) { - return candidate; - } - } - } - return nullptr; -} - -/* - Send the current event and event data to a widget. - Note: we will get the wrong coordinates if the widget is not a child of - the current event window (LEAVE events between windows). - */ -static int pen_send(Fl_Widget *w, int event, State trigger, bool &copied) { - // Copy most event data only once - if (!copied) { - copy_state(); - copied = true; - } - // Copy the top_window coordinates again as they may change when w changes - Fl::e_x = e.x = ev.x; - Fl::e_y = e.y = ev.y; - // Send the event. - e.trigger = trigger; - return w->handle(event); -} - -/* - Send an event to all subscribers. - */ -static int pen_send_all(int event, State trigger) { - bool copied = false; - // use local value because handler may still change ev values - for (auto &it: subscriber_list_) { - auto w = it.second->widget(); - if (w) - pen_send(w, event, trigger, copied); - } - return 1; -} - -/* - Convert the NSEvent button number to Fl::Pen::State, - */ -static State button_to_trigger(POINTER_BUTTON_CHANGE_TYPE button, bool down) { - switch (button) { - case POINTER_CHANGE_FIRSTBUTTON_DOWN: - case POINTER_CHANGE_FIRSTBUTTON_UP: - if ( (ev.state & (State::ERASER_DOWN | State::ERASER_HOVERS)) != State::NONE ) { - return down ? State::ERASER_DOWN : State::ERASER_HOVERS; - } else { - return down ? State::TIP_DOWN : State::TIP_HOVERS; - } - case POINTER_CHANGE_SECONDBUTTON_DOWN: - case POINTER_CHANGE_SECONDBUTTON_UP: - return State::BUTTON0; - case POINTER_CHANGE_THIRDBUTTON_DOWN: - case POINTER_CHANGE_THIRDBUTTON_UP: - return State::BUTTON1; - case POINTER_CHANGE_FOURTHBUTTON_DOWN: - case POINTER_CHANGE_FOURTHBUTTON_UP: - return State::BUTTON2; - case POINTER_CHANGE_FIFTHBUTTON_DOWN: - case POINTER_CHANGE_FIFTHBUTTON_UP: - return State::BUTTON3; - default: return State::NONE; - } -} - -/* - Handle events coming from the Win32 API. - WM_TABLET (Windows 2000 and up) - WM_POINTER (Windows 8 and up) - https://learn.microsoft.com/en-us/windows/win32/inputmsg/messages-and-notifications-portal - #if(WINVER >= 0x0602) ... #endif - \return -1 if we did not handle the event and want the main event handler to call DefWindowProc() - \return any other value that will then be return from WndProc() directly. - */ -LRESULT fl_win32_tablet_handler(MSG& msg) { - auto message = msg.message; - if (message < WM_NCPOINTERUPDATE || message > WM_POINTERROUTEDRELEASED) { - return -1; - } - - Fl_Window *eventWindow = fl_find(msg.hwnd); // can be nullptr - bool is_proximity = false; - bool is_down = false; - bool is_up = false; - bool is_motion = false; - - switch (msg.message) { - case WM_NCPOINTERDOWN: // pen pushed over window decoration, don't care - case WM_NCPOINTERUP: // pen released over window decoration, don't care - case WM_NCPOINTERUPDATE: // pen moved over decoration, don't care - case WM_POINTERACTIVATE: // shall the pointer activate an inactive window? - return -1; // let the system handle this forwarding this to DefWindowProc - - case WM_POINTERENTER: // pointer moved into window area from top or sides - is_proximity = true; - break; - case WM_POINTERLEAVE: // left window area to top or sides - is_proximity = true; - break; - - - case WM_POINTERDOWN: - is_down = true; - break; - case WM_POINTERUP: - is_up = true; - break; - case WM_POINTERUPDATE: - is_motion = true; - break; - - case WM_POINTERCAPTURECHANGED: - case WM_TOUCHHITTESTING: - case WM_POINTERWHEEL: - case WM_POINTERHWHEEL: - case DM_POINTERHITTEST: - case WM_POINTERROUTEDTO: - case WM_POINTERROUTEDAWAY: - case WM_POINTERROUTEDRELEASED: - default: - // printf("Windows message: msg=0x%04X wParam=0x%08X lParam=0x%08X\n", - // msg.message, (unsigned)msg.wParam, (unsigned)msg.lParam); - return -1; - } - // printf(" msg=0x%04X wParam=0x%08X lParam=0x%08X\n", - // msg.message, (unsigned)msg.wParam, (unsigned)msg.lParam); - - POINTER_PEN_INFO info; - BOOL has_position = GetPointerPenInfo( - GET_POINTERID_WPARAM(msg.wParam), - &info - ); - // if (has_position && info.pointerInfo.ButtonChangeType!=0) { - // printf(" pointerFlags: %08x [", (unsigned)info.pointerInfo.pointerFlags); - // if (info.pointerInfo.pointerFlags & POINTER_FLAG_FIRSTBUTTON) printf(" 1ST"); - // if (info.pointerInfo.pointerFlags & POINTER_FLAG_SECONDBUTTON) printf(" 2ND"); - // if (info.pointerInfo.pointerFlags & POINTER_FLAG_THIRDBUTTON) printf(" 3RD"); - // if (info.pointerInfo.pointerFlags & POINTER_FLAG_FOURTHBUTTON) printf(" 4TH"); - // if (info.pointerInfo.pointerFlags & POINTER_FLAG_FIFTHBUTTON) printf(" 5TH"); - // printf(" ]\n penFlags: %08x [", (unsigned)info.penFlags); - // if (info.penFlags & PEN_FLAG_BARREL) printf(" BARREL"); - // if (info.penFlags & PEN_FLAG_INVERTED) printf(" INVERTED"); - // if (info.penFlags & PEN_FLAG_ERASER) printf(" ERASER"); - // printf(" ]\n penMask: %08x ButtonChangeType: %d\n", - // (unsigned)info.penMask, info.pointerInfo.ButtonChangeType); - // } - - // Event has extended pen data set: - if (has_position) { - // Get the position data. - double s = Fl::screen_driver()->scale(0); - double ex = info.pointerInfo.ptPixelLocation.x/s; - double ey = info.pointerInfo.ptPixelLocation.y/s; - // Go from global coordinates to event window coordinates - Fl_Widget *p = eventWindow; - while (p) { - if (p->as_window()) { - ex -= p->x(); - ey -= p->y(); - } - p = p->parent(); - }; - ev.x = ex; - ev.y = ey; - ev.rx = info.pointerInfo.ptPixelLocation.x/s; - ev.ry = info.pointerInfo.ptPixelLocation.y/s; - if (!is_proximity) { - // Get the extended data. - if (info.penMask & PEN_MASK_PRESSURE) - ev.pressure = info.pressure / 1024.0; - if (info.penMask & PEN_MASK_TILT_X) - ev.tilt_x = -info.tiltX / 90.0; - if (info.penMask & PEN_MASK_TILT_Y) - ev.tilt_y = -info.tiltY / 90.0; - if (info.penMask & PEN_MASK_ROTATION) - ev.twist = info.rotation > 180 ? (info.rotation - 360) : info.rotation; - if (info.pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) - ev.proximity = 0.0; - else - ev.proximity = 1.0; - } - if (info.penFlags & PEN_FLAG_INVERTED) { - device_type_ = _FL_ERASER; - if (info.pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) - ev.state = State::ERASER_DOWN; - else - ev.state = State::ERASER_HOVERS; - } else { - device_type_ = _FL_PEN; - if (info.pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) - ev.state = State::TIP_DOWN; - else - ev.state = State::TIP_HOVERS; - } - // Add pen barrel button states - // Note: POINTER_FLAG_FIRSTBUTTON is the pen tip - // PEN_FLAG_BARREL and POINTER_FLAG_SECONDBUTTON both indicate the primary barrel button - if ((info.penFlags & PEN_FLAG_BARREL) || (info.pointerInfo.pointerFlags & POINTER_FLAG_SECONDBUTTON)) - ev.state |= State::BUTTON0; - // Note: the following code does not work very well with the Wayland driver - // More research is needed to find out how to get these button states reliably. - if (info.pointerInfo.pointerFlags & POINTER_FLAG_THIRDBUTTON) ev.state |= State::BUTTON1; - if (info.pointerInfo.pointerFlags & POINTER_FLAG_FOURTHBUTTON) ev.state |= State::BUTTON2; - if (info.pointerInfo.pointerFlags & POINTER_FLAG_FIFTHBUTTON) ev.state |= State::BUTTON3; - } - // printf(" %08x\n", (unsigned)ev.state); - if (is_proximity) { - ev.pen_id = GET_POINTERID_WPARAM(msg.wParam); - } - if ((msg.message == WM_POINTERENTER) || (msg.message == WM_POINTERLEAVE)) { - if (msg.message == WM_POINTERENTER) { - // Check if this is the first time we see this pen, or if the pen changed - if (current_pen_id_ != ev.pen_id) { - current_pen_id_ = ev.pen_id; - auto it = trait_list_.find(current_pen_id_); - if (it == trait_list_.end()) { // not found, create a new entry - trait_list_[current_pen_id_] = Trait::DRIVER_AVAILABLE; - trait_countdown_ = 5; - pen_send_all(Fl::Pen::DETECTED, State::NONE); - // printf("IN RANGE, NEW PEN\n"); - } else { - pen_send_all(Fl::Pen::CHANGED, State::NONE); - // printf("IN RANGE, CHANGED PEN\n"); - } - trait_list_[0] = trait_list_[current_pen_id_]; // set current pen traits - } else { - pen_send_all(Fl::Pen::IN_RANGE, State::NONE); - // printf("IN RANGE\n"); - } - } else { - pen_send_all(Fl::Pen::OUT_OF_RANGE, State::NONE); - // printf("OUT OF RANGE\n"); - } - } - - Fl_Widget *receiver = nullptr; - bool pushed = false; - bool event_data_copied = false; - - if (has_position) { - if (trait_countdown_) { - trait_countdown_--; - if (ev.tilt_x != 0.0) current_pen_trait_ |= Trait::TILT_X; - if (ev.tilt_y != 0.0) current_pen_trait_ |= Trait::TILT_Y; - if (ev.pressure != 1.0) current_pen_trait_ |= Trait::PRESSURE; - if (ev.barrel_pressure != 0.0) current_pen_trait_ |= Trait::BARREL_PRESSURE; - if (ev.pen_id != 0) current_pen_trait_ |= Trait::PEN_ID; - if (ev.twist != 0.0) current_pen_trait_ |= Trait::TWIST; - //if (ev.proximity != 0) current_pen_trait_ |= Trait::PROXIMITY; - trait_list_[current_pen_id_] = current_pen_trait_; - } - fl_xmousewin = eventWindow; - if (pushed_ && pushed_->widget() && (Fl::pushed() == pushed_->widget())) { - receiver = pushed_->widget(); - if (Fl::grab() && (Fl::grab() != receiver->top_window())) - return -1; - if (Fl::modal() && (Fl::modal() != receiver->top_window())) - return -1; - pushed = true; - } else { - if (Fl::grab() && (Fl::grab() != eventWindow)) - return -1; - if (Fl::modal() && (Fl::modal() != eventWindow)) - return -1; - auto bpen = below_pen_ ? below_pen_->widget() : nullptr; - auto bmouse = Fl::belowmouse(); - auto bpen_old = bmouse && (bmouse == bpen) ? bpen : nullptr; - auto bpen_now = find_below_pen(eventWindow, ev.x, ev.y); - - if (bpen_now != bpen_old) { - if (bpen_old) { - pen_send(bpen_old, Fl::Pen::LEAVE, State::NONE, event_data_copied); - } - below_pen_ = nullptr; - if (bpen_now) { - State state = (device_type_ == _FL_ERASER) ? State::ERASER_HOVERS : State::TIP_HOVERS; - if (pen_send(bpen_now, Fl::Pen::ENTER, state, event_data_copied)) { - below_pen_ = subscriber_list_[bpen_now]; - Fl::belowmouse(bpen_now); - } - } - } - - receiver = below_pen_ ? below_pen_->widget() : nullptr; - if (!receiver) - return -1; - } - } else { - // Proximity events were handled earlier. - } - - if (!receiver) - return -1; - - if (is_down) { - if (!pushed) { - pushed_ = subscriber_list_[receiver]; - Fl::pushed(receiver); - } - State trigger = button_to_trigger(info.pointerInfo.ButtonChangeType, true); - if (msg.message == WM_POINTERDOWN) { - Fl::e_is_click = 1; - _e_x_down = (int)ev.x; - _e_y_down = (int)ev.y; - - // Implement click counting using Windows system metrics - DWORD current_time = GetMessageTime(); - DWORD double_click_time = GetDoubleClickTime(); - int double_click_dx = GetSystemMetrics(SM_CXDOUBLECLK) / 2; - int double_click_dy = GetSystemMetrics(SM_CYDOUBLECLK) / 2; - - // Check if this is a multi-click: same trigger, within time and distance thresholds - if (trigger == last_click_trigger_ && - (current_time - last_click_time_) < double_click_time && - abs((int)ev.rx - last_click_x_) < double_click_dx && - abs((int)ev.ry - last_click_y_) < double_click_dy) { - Fl::e_clicks++; - } else { - Fl::e_clicks = 0; - } - - last_click_time_ = current_time; - last_click_x_ = (int)ev.rx; - last_click_y_ = (int)ev.ry; - last_click_trigger_ = trigger; - - pen_send(receiver, Fl::Pen::TOUCH, trigger, event_data_copied); - } else { - pen_send(receiver, Fl::Pen::BUTTON_PUSH, trigger, event_data_copied); - } - } else if (is_up) { - if ( (ev.state & State::ANY_DOWN) == State::NONE ) { - Fl::pushed(nullptr); - pushed_ = nullptr; - } - State trigger = button_to_trigger(info.pointerInfo.ButtonChangeType, true); - if (info.pointerInfo.ButtonChangeType == 0) - pen_send(receiver, Fl::Pen::LIFT, trigger, event_data_copied); - else - pen_send(receiver, Fl::Pen::BUTTON_RELEASE, trigger, event_data_copied); - } else if (is_motion) { - if ( Fl::e_is_click && - ( (fabs((int)ev.x - _e_x_down) > 5) || - (fabs((int)ev.y - _e_y_down) > 5) ) ) - Fl::e_is_click = 0; - if (pushed) { - pen_send(receiver, Fl::Pen::DRAW, State::NONE, event_data_copied); - } else { - pen_send(receiver, Fl::Pen::HOVER, State::NONE, event_data_copied); - } - } - // Always return 0 because at this point, we capture pen events and don't - // want mouse events anymore! - return 0; -} diff --git a/src/drivers/WinAPI/Fl_WinAPI_Printer_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Printer_Driver.cxx deleted file mode 100644 index 4b74c76cb..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Printer_Driver.cxx +++ /dev/null @@ -1,518 +0,0 @@ -// -// Printing support for Windows for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2024 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include "../GDI/Fl_GDI_Graphics_Driver.H" -#include -#include -#include -#include -#include -#include -#include -#include // for fl_win32_xid() -#include // fl_strdup() -#include -#include // DocumentProperties(), OpenPrinter(), ClosePrinter() - -extern HWND fl_window; - -/** Support for printing on the Windows platform */ -class Fl_WinAPI_Printer_Driver : public Fl_Paged_Device { - friend class Fl_Printer; -protected: - int abortPrint; - PRINTDLG pd; - HDC hPr; - int prerr; - int left_margin; - int top_margin; - void absolute_printable_rect(int *x, int *y, int *w, int *h); - Fl_WinAPI_Printer_Driver(void); - int begin_job(int pagecount = 0, int *frompage = NULL, int *topage = NULL, char **perr_message = NULL) FL_OVERRIDE; - int begin_page (void) FL_OVERRIDE; - int printable_rect(int *w, int *h) FL_OVERRIDE; - void margins(int *left, int *top, int *right, int *bottom) FL_OVERRIDE; - void origin(int *x, int *y) FL_OVERRIDE; - void origin(int x, int y) FL_OVERRIDE; - void scale (float scale_x, float scale_y = 0.) FL_OVERRIDE; - void rotate(float angle) FL_OVERRIDE; - void translate(int x, int y) FL_OVERRIDE; - void untranslate(void) FL_OVERRIDE; - int end_page (void) FL_OVERRIDE; - void end_job (void) FL_OVERRIDE; - ~Fl_WinAPI_Printer_Driver(void); -}; - -Fl_WinAPI_Printer_Driver::Fl_WinAPI_Printer_Driver(void) : Fl_Paged_Device() { - hPr = NULL; - driver(new Fl_GDI_Printer_Graphics_Driver); -} - -Fl_Paged_Device* Fl_Printer::newPrinterDriver(void) -{ - return new Fl_WinAPI_Printer_Driver(); -} - - -Fl_WinAPI_Printer_Driver::~Fl_WinAPI_Printer_Driver(void) { - if (hPr) end_job(); - delete driver(); -} - -static void WIN_SetupPrinterDeviceContext(HDC prHDC) -{ - if ( !prHDC ) return; - - fl_window = 0; - SetGraphicsMode(prHDC, GM_ADVANCED); // to allow for rotations - SetMapMode(prHDC, MM_ANISOTROPIC); - SetTextAlign(prHDC, TA_BASELINE|TA_LEFT); - SetBkMode(prHDC, TRANSPARENT); - // this matches 720 logical units to the number of device units in 10 inches of paper - // thus the logical unit is the point (= 1/72 inch) - SetWindowExtEx(prHDC, 720, 720, NULL); - SetViewportExtEx(prHDC, 10*GetDeviceCaps(prHDC, LOGPIXELSX), 10*GetDeviceCaps(prHDC, LOGPIXELSY), NULL); -} - - -class Fl_PDF_GDI_File_Surface : public Fl_WinAPI_Printer_Driver -{ -private: - static LPSTR pdf_printer_name_; -public: - char *doc_fname; - Fl_PDF_GDI_File_Surface(); - ~Fl_PDF_GDI_File_Surface() { if (doc_fname) free(doc_fname); } - int begin_job(const char *defaultname, - char **perr_message = NULL); - int begin_job(int, int*, int *, char **) FL_OVERRIDE {return 1;} // don't use - int begin_document(const char* outname, - enum Fl_Paged_Device::Page_Format format, - enum Fl_Paged_Device::Page_Layout layout, - char **perr_message); - void end_job() FL_OVERRIDE; -}; - -LPSTR Fl_PDF_GDI_File_Surface::pdf_printer_name_ = _strdup("Microsoft Print to PDF"); - -Fl_PDF_GDI_File_Surface::Fl_PDF_GDI_File_Surface() { - driver(new Fl_GDI_Graphics_Driver()); - doc_fname = NULL; -} - -Fl_Paged_Device *Fl_PDF_File_Surface::new_platform_pdf_surface_(const char ***pfname) { - Fl_PDF_GDI_File_Surface *surf = new Fl_PDF_GDI_File_Surface(); - *pfname = (const char**)&surf->doc_fname; - return surf; -} - -int Fl_PDF_File_Surface::begin_job(const char* defaultfilename, - char **perr_message) { - return ((Fl_PDF_GDI_File_Surface*)platform_surface_)->begin_job(defaultfilename, perr_message); -} - -int Fl_PDF_File_Surface::begin_document(const char* defaultfilename, - enum Fl_Paged_Device::Page_Format format, - enum Fl_Paged_Device::Page_Layout layout, - char **perr_message) { - return ((Fl_PDF_GDI_File_Surface*)platform_surface_)->begin_document(defaultfilename, format, layout, perr_message); -} - - -int Fl_PDF_GDI_File_Surface::begin_job(const char *defaultfname, char **perr_message) { - int err = 0; - abortPrint = FALSE; - - HANDLE hPr2; - err = OpenPrinterA(pdf_printer_name_, &hPr2, NULL); - if (err == 0) { - if (perr_message) { - int l = 240; - *perr_message = new char[l]; - snprintf(*perr_message, l, - "Class Fl_PDF_File_Surface requires printer '%s' available in Windows 10+.", - pdf_printer_name_); - } - return 1; - } - HWND hwndOwner = fl_win32_xid(Fl::first_window()); - LONG count = DocumentPropertiesA(hwndOwner, hPr2, pdf_printer_name_, NULL, NULL, 0); - if (count <= 0) { ClosePrinter(hPr2); return 1; } - char *buffer = new char[count]; - DEVMODEA *pDevMode = (DEVMODEA*)buffer; - memset(buffer, 0, count); - pDevMode->dmSize = (WORD)count; - count = DocumentPropertiesA(hwndOwner, hPr2, pdf_printer_name_, pDevMode, NULL, DM_OUT_BUFFER | DM_IN_PROMPT); - ClosePrinter(hPr2); - if (count == IDCANCEL || count < 0) { delete[] buffer; return 1; } - - Fl_Native_File_Chooser fnfc; - fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); - fnfc.filter("PDF\t*.pdf\n"); - if (defaultfname && strlen(defaultfname) > 0) fnfc.preset_file(defaultfname); - fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM); - if (fnfc.show() == 0) this->hPr = CreateDCA(NULL, pdf_printer_name_, NULL, pDevMode); - delete[] buffer; - if (!this->hPr) return 1; - DOCINFOW di; - wchar_t docName [256]; - wchar_t outName [256]; - fl_utf8towc("FLTK", 4, docName, 256); - fl_utf8towc(fnfc.filename(), (unsigned int)strlen(fnfc.filename()), outName, 256); - memset(&di, 0, sizeof(DOCINFOW)); - di.cbSize = sizeof(DOCINFOW); - di.lpszDocName = (LPCWSTR)docName; - di.lpszOutput = (LPCWSTR)outName; - err = StartDocW(this->hPr, &di); - if (err <= 0) { - DWORD dw = GetLastError(); - DeleteDC(this->hPr); - this->hPr = NULL; - if (dw != ERROR_CANCELLED) { - if (perr_message) { - int l = 40; - *perr_message = new char[l]; - snprintf(*perr_message, l, "Error %lu in StartDoc() call", dw); - } - return 2; - } - return 1; - } - x_offset = 0; - y_offset = 0; - WIN_SetupPrinterDeviceContext(this->hPr); - driver()->gc(this->hPr); - doc_fname = fl_strdup(fnfc.filename()); - return 0; -} - - -int Fl_PDF_GDI_File_Surface::begin_document(const char* outfname, - enum Fl_Paged_Device::Page_Format format, - enum Fl_Paged_Device::Page_Layout layout, - char **perr_message) { - int err = 0; - abortPrint = FALSE; - - DEVMODEA inDevMode; - memset(&inDevMode, 0, sizeof(DEVMODEA)); inDevMode.dmSize = sizeof(DEVMODEA); - inDevMode.dmOrientation = (layout == PORTRAIT ? DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE); - inDevMode.dmPaperSize = (format == A4 ? DMPAPER_A4 : DMPAPER_LETTER); - inDevMode.dmFields = DM_ORIENTATION | DM_PAPERSIZE ; - - this->hPr = CreateDCA(NULL, pdf_printer_name_, NULL, &inDevMode); - if (!this->hPr) { - if (perr_message) { - int l = 150; - *perr_message = new char[l]; - snprintf(*perr_message, l, "Class Fl_PDF_File_Surface requires printer '%s'.", - pdf_printer_name_); - } - return 2; - } - DOCINFOW di; - wchar_t docName[256]; - wchar_t outName[256]; - fl_utf8towc("FLTK", 4, docName, 256); - memset(&di, 0, sizeof(DOCINFOW)); - di.cbSize = sizeof(DOCINFOW); - di.lpszDocName = (LPCWSTR)docName; - di.lpszOutput = (LPCWSTR)outName; - fl_utf8towc(outfname, (unsigned int)strlen(outfname), outName, 256); - err = StartDocW(hPr, &di); - if (err <= 0) { - DWORD dw = GetLastError(); - DeleteDC(this->hPr); - this->hPr = NULL; - if (perr_message) { - int l = 50; - *perr_message = new char[l]; - snprintf(*perr_message, l, "Error %lu in StartDoc() call", dw); - } - return 2; - } - x_offset = 0; - y_offset = 0; - WIN_SetupPrinterDeviceContext(this->hPr); - driver()->gc(this->hPr); - doc_fname = fl_strdup(outfname); - return 0; -} - - -int Fl_WinAPI_Printer_Driver::begin_job (int pagecount, int *frompage, int *topage, char **perr_message) -// returns 0 iff OK -{ - if (pagecount == 0) pagecount = 10000; - DOCINFO di; - char docName [256]; - int err = 0; - - abortPrint = FALSE; - memset (&pd, 0, sizeof (PRINTDLG)); - pd.lStructSize = sizeof (PRINTDLG); - pd.hwndOwner = GetForegroundWindow(); - pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE | PD_NOSELECTION; - pd.nMinPage = 1; - pd.nMaxPage = pagecount; - BOOL b = PrintDlg (&pd); - if (pd.hwndOwner) { // restore the correct state of mouse buttons and keyboard modifier keys (STR #3221) - WNDPROC windproc = (WNDPROC)GetWindowLongPtrW(pd.hwndOwner, GWLP_WNDPROC); - CallWindowProc(windproc, pd.hwndOwner, WM_ACTIVATEAPP, 1, 0); - } - if (b != 0) { - hPr = pd.hDC; - if (hPr != NULL) { - strcpy (docName, "FLTK"); - memset(&di, 0, sizeof(DOCINFO)); - di.cbSize = sizeof (DOCINFO); - di.lpszDocName = (LPCSTR) docName; - prerr = StartDoc (hPr, &di); - if (prerr < 1) { - abortPrint = TRUE; - DWORD dw = GetLastError(); - err = (dw == ERROR_CANCELLED ? 1 : 2); - if (perr_message && err == 2) { - wchar_t *lpMsgBuf; - DWORD retval = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR) &lpMsgBuf, - 0, NULL); - if (retval) { - unsigned srclen = lstrlenW(lpMsgBuf); - while (srclen > 0 && (lpMsgBuf[srclen-1] == '\n' || lpMsgBuf[srclen-1] == '\r')) srclen--; - unsigned l = fl_utf8fromwc(NULL, 0, lpMsgBuf, srclen); - *perr_message = new char[l+51]; - snprintf(*perr_message, l+51, "begin_job() failed with error %lu: ", dw); - fl_utf8fromwc(*perr_message + strlen(*perr_message), l+1, lpMsgBuf, srclen); - LocalFree(lpMsgBuf); - } - } - } - } - } else { - err = 1; - } - if(!err) { - if((pd.Flags & PD_PAGENUMS) != 0 ) { - if (frompage) *frompage = pd.nFromPage; - if (topage) *topage = pd.nToPage; - } - else { - if (frompage) *frompage = 1; - if (topage) *topage = pagecount; - } - x_offset = 0; - y_offset = 0; - WIN_SetupPrinterDeviceContext (hPr); - driver()->gc(hPr); - } - return err; -} - -void Fl_PDF_GDI_File_Surface::end_job(void) -{ - if (hPr != NULL) { - if (! abortPrint) { - if (EndDoc (hPr) <= 0) { - fl_message ("Error in EndDoc() call"); - } - DeleteDC (hPr); - } - hPr = NULL; - } -} - -void Fl_WinAPI_Printer_Driver::end_job (void) -{ - if (hPr != NULL) { - if (! abortPrint) { - prerr = EndDoc (hPr); - if (prerr < 0) { - fl_alert ("EndDoc error %d", prerr); - } - } - DeleteDC (hPr); - if (pd.hDevMode != NULL) { - GlobalFree (pd.hDevMode); - } - if (pd.hDevNames != NULL) { - GlobalFree (pd.hDevNames); - } - } - hPr = NULL; -} - -void Fl_WinAPI_Printer_Driver::absolute_printable_rect(int *x, int *y, int *w, int *h) -{ - POINT physPageSize; - POINT pixelsPerInch; - XFORM transform; - - if (hPr == NULL) return; - HDC gc = (HDC)driver()->gc(); - GetWorldTransform(gc, &transform); - ModifyWorldTransform(gc, NULL, MWT_IDENTITY); - SetWindowOrgEx(gc, 0, 0, NULL); - - physPageSize.x = GetDeviceCaps(hPr, HORZRES); - physPageSize.y = GetDeviceCaps(hPr, VERTRES); - DPtoLP(hPr, &physPageSize, 1); - *w = physPageSize.x + 1; - *h = physPageSize.y + 1; - pixelsPerInch.x = GetDeviceCaps(hPr, LOGPIXELSX); - pixelsPerInch.y = GetDeviceCaps(hPr, LOGPIXELSY); - DPtoLP(hPr, &pixelsPerInch, 1); - left_margin = (pixelsPerInch.x / 4); - *w -= (pixelsPerInch.x / 2); - top_margin = (pixelsPerInch.y / 4); - *h -= (pixelsPerInch.y / 2); - - *x = left_margin; - *y = top_margin; - origin(x_offset, y_offset); - SetWorldTransform(gc, &transform); -} - -void Fl_WinAPI_Printer_Driver::margins(int *left, int *top, int *right, int *bottom) -{ - int x = 0, y = 0, w = 0, h = 0; - absolute_printable_rect(&x, &y, &w, &h); - if (left) *left = x; - if (top) *top = y; - if (right) *right = x; - if (bottom) *bottom = y; -} - -int Fl_WinAPI_Printer_Driver::printable_rect(int *w, int *h) -{ - int x, y; - absolute_printable_rect(&x, &y, w, h); - return 0; -} - -int Fl_WinAPI_Printer_Driver::begin_page (void) -{ - int rsult, w, h; - - rsult = 0; - if (hPr != NULL) { - Fl_Surface_Device::push_current(this); - WIN_SetupPrinterDeviceContext (hPr); - prerr = StartPage (hPr); - if (prerr < 0) { - Fl_Surface_Device::pop_current(); - fl_alert ("StartPage error %d", prerr); - rsult = 1; - } - printable_rect(&w, &h); - origin(0, 0); - fl_clip_region(0); - } - return rsult; -} - -void Fl_WinAPI_Printer_Driver::origin (int deltax, int deltay) -{ - SetWindowOrgEx( (HDC)driver()->gc(), - left_margin - deltax, - top_margin - deltay, NULL); - x_offset = deltax; - y_offset = deltay; -} - -void Fl_WinAPI_Printer_Driver::scale (float scalex, float scaley) -{ - if (scaley == 0.) scaley = scalex; - int w, h; - SetWindowExtEx((HDC)driver()->gc(), (int)(720 / scalex + 0.5), (int)(720 / scaley + 0.5), NULL); - printable_rect(&w, &h); - origin(0, 0); -} - -void Fl_WinAPI_Printer_Driver::rotate (float rot_angle) -{ - XFORM mat; - float angle; - angle = (float) - (rot_angle * M_PI / 180.); - mat.eM11 = (float)cos(angle); - mat.eM12 = (float)sin(angle); - mat.eM21 = - mat.eM12; - mat.eM22 = mat.eM11; - mat.eDx = mat.eDy = 0; - SetWorldTransform((HDC)driver()->gc(), &mat); -} - -int Fl_WinAPI_Printer_Driver::end_page (void) -{ - int rsult; - - rsult = 0; - if (hPr != NULL) { - Fl_Surface_Device::pop_current(); - prerr = EndPage (hPr); - if (prerr < 0) { - abortPrint = TRUE; - fl_alert ("EndPage error %d", prerr); - rsult = 1; - } - else { // make sure rotation is not transferred to next page - ModifyWorldTransform(hPr, NULL, MWT_IDENTITY); - } - } - return rsult; -} - -static int translate_stack_depth = 0; -const int translate_stack_max = 5; -static int translate_stack_x[translate_stack_max]; -static int translate_stack_y[translate_stack_max]; - -static void do_translate(int x, int y, HDC gc) -{ - XFORM tr; - tr.eM11 = tr.eM22 = 1; - tr.eM12 = tr.eM21 = 0; - tr.eDx = (FLOAT) x; - tr.eDy = (FLOAT) y; - ModifyWorldTransform(gc, &tr, MWT_LEFTMULTIPLY); -} - -void Fl_WinAPI_Printer_Driver::translate (int x, int y) -{ - do_translate(x, y, (HDC)driver()->gc()); - if (translate_stack_depth < translate_stack_max) { - translate_stack_x[translate_stack_depth] = x; - translate_stack_y[translate_stack_depth] = y; - translate_stack_depth++; - } -} - -void Fl_WinAPI_Printer_Driver::untranslate (void) -{ - if (translate_stack_depth > 0) { - translate_stack_depth--; - do_translate( - translate_stack_x[translate_stack_depth], - translate_stack_y[translate_stack_depth], (HDC)driver()->gc() ); - } -} - -void Fl_WinAPI_Printer_Driver::origin(int *x, int *y) -{ - Fl_Paged_Device::origin(x, y); -} - - diff --git a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H deleted file mode 100644 index b038fc5f1..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H +++ /dev/null @@ -1,104 +0,0 @@ -// -// Windows screen interface for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_WinAPI_Screen_Driver.H - \brief Definition of Windows screen interface. - */ - -#ifndef FL_WINAPI_SCREEN_DRIVER_H -#define FL_WINAPI_SCREEN_DRIVER_H - -#include "../../Fl_Screen_Driver.H" -#include - -class Fl_Window; - - -class Fl_WinAPI_Screen_Driver : public Fl_Screen_Driver -{ -protected: - RECT screens[MAX_SCREENS]; - RECT work_area[MAX_SCREENS]; - float scale_of_screen[MAX_SCREENS]; - - static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM); - BOOL screen_cb(HMONITOR mon, HDC, LPRECT r); - int get_mouse_unscaled(int &mx, int &my); - -public: - float dpi[MAX_SCREENS][2]; - enum APP_SCALING_CAPABILITY scaling_capability; - void update_scaling_capability(); - Fl_WinAPI_Screen_Driver(); - // --- display management - int visual(int flags) FL_OVERRIDE; - // --- screen configuration - void init() FL_OVERRIDE; - int x() FL_OVERRIDE; - int y() FL_OVERRIDE; - int w() FL_OVERRIDE; - int h() FL_OVERRIDE; - void screen_xywh(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - void screen_xywh_unscaled(int &X, int &Y, int &W, int &H, int n); - void screen_dpi(float &h, float &v, int n=0) FL_OVERRIDE; - int screen_num_unscaled(int x, int y); - void screen_work_area(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; - // --- audible output - void beep(int type) FL_OVERRIDE; - // --- global events - void flush() FL_OVERRIDE; - void grab(Fl_Window* win) FL_OVERRIDE; - // --- global colors - void get_system_colors() FL_OVERRIDE; - int dnd(int unused) FL_OVERRIDE; - int compose(int &del) FL_OVERRIDE; - Fl_RGB_Image *read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win, bool may_capture_subwins, bool *did_capture_subwins) FL_OVERRIDE; - Fl_RGB_Image *read_win_rectangle_unscaled(int X, int Y, int w, int h, Fl_Window *win); - int get_mouse(int &x, int &y) FL_OVERRIDE; - void enable_im() FL_OVERRIDE; - void disable_im() FL_OVERRIDE; - void open_display_platform() FL_OVERRIDE; - void offscreen_size(Fl_Offscreen off, int &width, int &height) FL_OVERRIDE; - APP_SCALING_CAPABILITY rescalable() FL_OVERRIDE { - return scaling_capability; - } - float scale(int n) FL_OVERRIDE { - return scale_of_screen[n]; - } - void scale(int n, float f) FL_OVERRIDE { - scale_of_screen[n] = f; - } - void desktop_scale_factor() FL_OVERRIDE; - void default_icons(const Fl_RGB_Image *icons[], int count) FL_OVERRIDE; - // this one is implemented in Fl_win32.cxx - void copy(const char *stuff, int len, int clipboard, const char *type) FL_OVERRIDE; - // this one is implemented in Fl_win32.cxx - void paste(Fl_Widget &receiver, int clipboard, const char *type) FL_OVERRIDE; - // this one is implemented in Fl_win32.cxx - int clipboard_contains(const char *type) FL_OVERRIDE; - // this one is implemented in Fl_win32.cxx - void clipboard_notify_change() FL_OVERRIDE; - // this one is implemented in Fl_win32.cxx - void set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) FL_OVERRIDE; - // these two are implemented in Fl_get_key_win32.cxx - int event_key(int) FL_OVERRIDE; - int get_key(int) FL_OVERRIDE; - float base_scale(int numscreen) FL_OVERRIDE; -}; - - -#endif // FL_WINAPI_SCREEN_DRIVER_H diff --git a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx deleted file mode 100644 index b1ddc9f91..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx +++ /dev/null @@ -1,493 +0,0 @@ -// -// Windows screen interface for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include -#include "Fl_WinAPI_Screen_Driver.H" -#include "../GDI/Fl_Font.H" -#include -#include -#include -#include -#include - - -// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx -extern const char *fl_fg; -extern const char *fl_bg; -extern const char *fl_bg2; -// end of extern additions workaround - - -#if !defined(HMONITOR_DECLARED) && (_WIN32_WINNT < 0x0500) -# define COMPILE_MULTIMON_STUBS -# include -#endif // !HMONITOR_DECLARED && _WIN32_WINNT < 0x0500 - -static Fl_Text_Editor::Key_Binding extra_bindings[] = { - // Define Windows specific accelerators... - { 'y', FL_CTRL, Fl_Text_Editor::kf_redo ,0}, - { 0, 0, 0 ,0} -}; - - -Fl_WinAPI_Screen_Driver::Fl_WinAPI_Screen_Driver() : Fl_Screen_Driver() { - text_editor_extra_key_bindings = extra_bindings; - for (int i = 0; i < MAX_SCREENS; i++) scale_of_screen[i] = 1; - scaling_capability = SYSTEMWIDE_APP_SCALING; -} - -int Fl_WinAPI_Screen_Driver::visual(int flags) -{ - fl_GetDC(0); - if (flags & FL_DOUBLE) return 0; - HDC gc = (HDC)Fl_Graphics_Driver::default_driver().gc(); - if (!(flags & FL_INDEX) && - GetDeviceCaps(gc,BITSPIXEL) <= 8) return 0; - if ((flags & FL_RGB8) && GetDeviceCaps(gc,BITSPIXEL)<24) return 0; - return 1; -} - - -// We go the much more difficult route of individually picking some multi-screen -// functions from the USER32.DLL . If these functions are not available, we -// will gracefully fall back to single monitor support. -// -// If we were to insist on the existence of "EnumDisplayMonitors" and -// "GetMonitorInfoA", it would be impossible to use FLTK on Windows 2000 -// before SP2 or earlier. - -// BOOL EnumDisplayMonitors(HDC, LPCRECT, MONITORENUMPROC, LPARAM) -typedef BOOL(WINAPI* fl_edm_func)(HDC, LPCRECT, MONITORENUMPROC, LPARAM); -// BOOL GetMonitorInfo(HMONITOR, LPMONITORINFO) -typedef BOOL(WINAPI* fl_gmi_func)(HMONITOR, LPMONITORINFO); - -static fl_gmi_func fl_gmi = NULL; // used to get a proc pointer for GetMonitorInfoA - - -BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC hdc, LPRECT r, LPARAM d) -{ - Fl_WinAPI_Screen_Driver *drv = (Fl_WinAPI_Screen_Driver*)d; - return drv->screen_cb(mon, hdc, r); -} - - -BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC, LPRECT r) -{ - if (num_screens >= MAX_SCREENS) return TRUE; - - MONITORINFOEX mi; - mi.cbSize = sizeof(mi); - - // GetMonitorInfo(mon, &mi); - // (but we use our self-acquired function pointer instead) - if (fl_gmi(mon, &mi)) { - screens[num_screens] = mi.rcMonitor; - // If we also want to record the work area, we would also store mi.rcWork at this point - work_area[num_screens] = mi.rcWork; - num_screens++; - } - return TRUE; -} - - -void Fl_WinAPI_Screen_Driver::init() -{ - open_display(); - // Since not all versions of Windows include multiple monitor support, - // we do a run-time check for the required functions... - HMODULE hMod = GetModuleHandle("USER32.DLL"); - - if (hMod) { - // check that EnumDisplayMonitors is available - fl_edm_func fl_edm = (fl_edm_func)GetProcAddress(hMod, "EnumDisplayMonitors"); - - if (fl_edm) { - // we have EnumDisplayMonitors - do we also have GetMonitorInfoA ? - fl_gmi = (fl_gmi_func)GetProcAddress(hMod, "GetMonitorInfoA"); - if (fl_gmi) { - // We have GetMonitorInfoA, enumerate all the screens... - // EnumDisplayMonitors(0,0,screen_cb,0); - // (but we use our self-acquired function pointer instead) - // NOTE: num_screens is incremented in screen_cb so we must first reset it here... - num_screens = 0; - fl_edm(0, 0, screen_cb, (LPARAM)this); - return; - } - } - } - - // If we get here, assume we have 1 monitor... - num_screens = 1; - screens[0].top = 0; - screens[0].left = 0; - screens[0].right = GetSystemMetrics(SM_CXSCREEN); - screens[0].bottom = GetSystemMetrics(SM_CYSCREEN); - work_area[0] = screens[0]; -} - - -void Fl_WinAPI_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - if (n < 0 || n >= num_screens) n = 0; - X = int(work_area[n].left/scale_of_screen[n]); - Y = int(work_area[n].top/scale_of_screen[n]); - W = int((work_area[n].right - work_area[n].left)/scale_of_screen[n]); - H = int((work_area[n].bottom - work_area[n].top)/scale_of_screen[n]); -} - - -void Fl_WinAPI_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - - if ((n < 0) || (n >= num_screens)) - n = 0; - - if (num_screens > 0) { - X = int(screens[n].left/scale_of_screen[n]); - Y = int(screens[n].top/scale_of_screen[n]); - W = int((screens[n].right - screens[n].left)/scale_of_screen[n]); - H = int((screens[n].bottom - screens[n].top)/scale_of_screen[n]); - } else { - /* Fallback if something is broken... */ - X = 0; - Y = 0; - W = GetSystemMetrics(SM_CXSCREEN); - H = GetSystemMetrics(SM_CYSCREEN); - } -} - - -void Fl_WinAPI_Screen_Driver::screen_xywh_unscaled(int &X, int &Y, int &W, int &H, int n) { - if (num_screens < 0) init(); - if ((n < 0) || (n >= num_screens)) n = 0; - X = screens[n].left; - Y = screens[n].top; - W = screens[n].right - screens[n].left; - H = screens[n].bottom - screens[n].top; -}; - - -void Fl_WinAPI_Screen_Driver::screen_dpi(float &h, float &v, int n) -{ - if (num_screens < 0) init(); - h = v = 0.0f; - if (n >= 0 && n < num_screens) { - h = float(dpi[n][0]); - v = float(dpi[n][1]); - } -} - - -int Fl_WinAPI_Screen_Driver::x() -{ - /*RECT r; - - SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); - return r.left;*/ - int X, Y, W, H; - screen_work_area(X, Y, W, H, 0); - return X; -} - - -int Fl_WinAPI_Screen_Driver::y() -{ - /*RECT r; - - SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); - return r.top;*/ - int X, Y, W, H; - screen_work_area(X, Y, W, H, 0); - return Y; -} - - -int Fl_WinAPI_Screen_Driver::h() -{ - /*RECT r; - - SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); - return r.bottom - r.top;*/ - int X, Y, W, H; - screen_work_area(X, Y, W, H, 0); - return H; -} - - -int Fl_WinAPI_Screen_Driver::w() -{ - /*RECT r; - - SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); - return r.right - r.left;*/ - int X, Y, W, H; - screen_work_area(X, Y, W, H, 0); - return W; -} - - -// Implements fl_beep(). See documentation in src/fl_ask.cxx. -void Fl_WinAPI_Screen_Driver::beep(int type) -{ - switch (type) { - case FL_BEEP_QUESTION : - case FL_BEEP_PASSWORD : - MessageBeep(MB_ICONQUESTION); - break; - case FL_BEEP_MESSAGE : - MessageBeep(MB_ICONASTERISK); - break; - case FL_BEEP_NOTIFICATION : - MessageBeep(MB_ICONASTERISK); - break; - case FL_BEEP_ERROR : - MessageBeep(MB_ICONERROR); - break; - default : - MessageBeep(0xFFFFFFFF); - break; - } -} - - -void Fl_WinAPI_Screen_Driver::flush() -{ - GdiFlush(); -} - - -extern void fl_fix_focus(); // in Fl.cxx - -// We have to keep track of whether we have captured the mouse, since -// Windows shows little respect for this... Grep for fl_capture to -// see where and how this is used. -extern HWND fl_capture; - - -void Fl_WinAPI_Screen_Driver::grab(Fl_Window* win) -{ - if (win) { - if (!Fl::grab_) { - SetActiveWindow(fl_capture = fl_xid(Fl::first_window())); - SetCapture(fl_capture); - } - Fl::grab_ = win; - } else { - if (Fl::grab_) { - fl_capture = 0; - ReleaseCapture(); - Fl::grab_ = 0; - fl_fix_focus(); - } - } -} - - -static void set_selection_color(uchar r, uchar g, uchar b) -{ - Fl::set_color(FL_SELECTION_COLOR,r,g,b); -} - - -static void getsyscolor(int what, const char* arg, void (*func)(uchar,uchar,uchar)) -{ - if (arg) { - uchar r,g,b; - if (!fl_parse_color(arg, r,g,b)) - Fl::error("Unknown color: %s", arg); - else - func(r,g,b); - } else { - DWORD x = GetSysColor(what); - func(uchar(x&255), uchar(x>>8), uchar(x>>16)); - } -} - - -void Fl_WinAPI_Screen_Driver::get_system_colors() -{ - if (!bg2_set) getsyscolor(COLOR_WINDOW, fl_bg2,Fl::background2); - if (!fg_set) getsyscolor(COLOR_WINDOWTEXT, fl_fg, Fl::foreground); - if (!bg_set) getsyscolor(COLOR_BTNFACE, fl_bg, Fl::background); - getsyscolor(COLOR_HIGHLIGHT, 0, set_selection_color); -} - - -int Fl_WinAPI_Screen_Driver::compose(int &del) { - unsigned char ascii = (unsigned char)Fl::e_text[0]; - /* WARNING: The [AltGr] key on international keyboards sets FL_CTRL. - 2nd line in condition below asks [AltGr] key (a.k.a. VK_RMENU) not to be down. - */ - int condition = (Fl::e_state & (FL_ALT | FL_META | FL_CTRL)) && !(ascii & 128) && - !( (Fl::e_state & FL_CTRL) && (GetAsyncKeyState(VK_RMENU) >> 15) ); - if (condition) { // this stuff is to be treated as a function key - del = 0; - return 0; - } - del = Fl::compose_state; - Fl::compose_state = 0; - // Only insert non-control characters: - if ( (!Fl::compose_state) && ! (ascii & ~31 && ascii!=127)) { - return 0; - } - return 1; -} - - -Fl_RGB_Image * // O - image or NULL if failed -Fl_WinAPI_Screen_Driver::read_win_rectangle( - int X, // I - Left position - int Y, // I - Top position - int w, // I - Width of area to read - int h, // I - Height of area to read - Fl_Window *win, // I - window to capture from or NULL to capture from current offscreen - bool may_capture_subwins, bool *did_capture_subwins) -{ - float s = Fl_Surface_Device::surface()->driver()->scale(); - int ws, hs; - if (int(s) == s) { ws = w * int(s); hs = h * int(s);} - else { - ws = Fl_Scalable_Graphics_Driver::floor(X+w, s) - Fl_Scalable_Graphics_Driver::floor(X, s), - hs = Fl_Scalable_Graphics_Driver::floor(Y+h, s) - Fl_Scalable_Graphics_Driver::floor(Y, s); - if (ws < 1) ws = 1; - if (hs < 1) hs = 1; - } - return read_win_rectangle_unscaled(Fl_Scalable_Graphics_Driver::floor(X, s), Fl_Scalable_Graphics_Driver::floor(Y, s), ws, hs, win); -} - -Fl_RGB_Image *Fl_WinAPI_Screen_Driver::read_win_rectangle_unscaled(int X, int Y, int w, int h, Fl_Window *win) -{ - // Depth of image is always 3 here - - // Grab all of the pixels in the image... - - // Assure that we are not trying to read non-existing data. If it is so, the - // function should still work, but the out-of-bounds part of the image is - // untouched (initialized with the alpha value or 0 (black), resp.). - - int ww = w; // We need the original width for output data line size - - int shift_x = 0; // X target shift if X modified - int shift_y = 0; // Y target shift if X modified - - if (X < 0) { - shift_x = -X; - w += X; - X = 0; - } - if (Y < 0) { - shift_y = -Y; - h += Y; - Y = 0; - } - - if (h < 1 || w < 1) return 0; // nothing to copy - - // Allocate and initialize the image data array - size_t arraySize = ((size_t)w * h) * 3; - uchar *p = new uchar[arraySize]; - memset(p, 0, arraySize); - - int line_size = ((3*w+3)/4) * 4; // each line is aligned on a DWORD (4 bytes) - uchar *dib = new uchar[line_size*h]; // create temporary buffer to read DIB - - // fill in bitmap info for GetDIBits - - BITMAPINFO bi; - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = w; - bi.bmiHeader.biHeight = -h; // negative => top-down DIB - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 24; // 24 bits RGB - bi.bmiHeader.biCompression = BI_RGB; - bi.bmiHeader.biSizeImage = 0; - bi.bmiHeader.biXPelsPerMeter = 0; - bi.bmiHeader.biYPelsPerMeter = 0; - bi.bmiHeader.biClrUsed = 0; - bi.bmiHeader.biClrImportant = 0; - - // copy bitmap from original DC (Window, Fl_Offscreen, ...) - if (win && Fl_Window::current() != win) win->make_current(); - HDC gc = (HDC)fl_graphics_driver->gc(); - HDC hdc = CreateCompatibleDC(gc); - HBITMAP hbm = CreateCompatibleBitmap(gc,w,h); - - int save_dc = SaveDC(hdc); // save context for cleanup - SelectObject(hdc,hbm); // select bitmap - BitBlt(hdc,0,0,w,h,gc,X,Y,SRCCOPY); // copy image section to DDB - - // copy RGB image data to the allocated DIB - - GetDIBits(hdc, hbm, 0, h, dib, (BITMAPINFO *)&bi, DIB_RGB_COLORS); - - // finally copy the image data to the user buffer - - for (int j = 0; jalloc_array = 1; - return rgb; -} - - -void Fl_WinAPI_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height) -{ - BITMAP bitmap; - if ( GetObject((HBITMAP)off, sizeof(BITMAP), &bitmap) ) { - width = bitmap.bmWidth; - height = bitmap.bmHeight; - } -} - -//NOTICE: returns -1 if x,y is not in any screen -int Fl_WinAPI_Screen_Driver::screen_num_unscaled(int x, int y) -{ - int screen = -1; - if (num_screens < 0) init(); - for (int i = 0; i < num_screens; i ++) { - if (x >= screens[i].left && x < screens[i].right && - y >= screens[i].top && y < screens[i].bottom) { - screen = i; - break; - } - } - return screen; -} - - -float Fl_WinAPI_Screen_Driver::base_scale(int numscreen) { - return float(dpi[numscreen][0] / 96.); -} diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H deleted file mode 100644 index 016cad9fa..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H +++ /dev/null @@ -1,124 +0,0 @@ -// -// Windows system driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_WinAPI_System_Driver.H - \brief Definition of Windows system driver. - */ - -#ifndef FL_WINAPI_SYSTEM_DRIVER_H -#define FL_WINAPI_SYSTEM_DRIVER_H - -#include "../../Fl_System_Driver.H" -#include -#include // strdup - -/* - Move everything here that manages the system interface. - - There is exactly one system driver. - - - filename and pathname management - - directory and file access - - system time and system timer - - multithreading - - string management - */ - -class Fl_WinAPI_System_Driver : public Fl_System_Driver -{ -public: - void warning(const char *format, va_list args) FL_OVERRIDE; - void error(const char *format, va_list args) FL_OVERRIDE; - void fatal(const char *format, va_list args) FL_OVERRIDE; - char *utf2mbcs(const char *s) FL_OVERRIDE; - char *getenv(const char *var) FL_OVERRIDE; - int putenv(const char *var) FL_OVERRIDE; - int open(const char *fnam, int oflags, int pmode) FL_OVERRIDE; - int open_ext(const char *fnam, int binary, int oflags, int pmode) FL_OVERRIDE; - FILE *fopen(const char *fnam, const char *mode) FL_OVERRIDE; - int system(const char *cmd) FL_OVERRIDE; - int execvp(const char *file, char *const *argv) FL_OVERRIDE; - int chmod(const char *fnam, int mode) FL_OVERRIDE; - int access(const char *fnam, int mode) FL_OVERRIDE; - int flstat(const char *fnam, struct stat *b) FL_OVERRIDE; - char *getcwd(char *b, int l) FL_OVERRIDE; - int chdir(const char *path) FL_OVERRIDE; - int unlink(const char *fnam) FL_OVERRIDE; - int mkdir(const char *fnam, int mode) FL_OVERRIDE; - int rmdir(const char *fnam) FL_OVERRIDE; - int rename(const char *fnam, const char *newnam) FL_OVERRIDE; - // Windows commandline argument conversion to UTF-8 - int args_to_utf8(int argc, char ** &argv) FL_OVERRIDE; - // Windows specific UTF-8 conversions - unsigned utf8towc(const char *src, unsigned srclen, wchar_t* dst, unsigned dstlen) FL_OVERRIDE; - unsigned utf8fromwc(char *dst, unsigned dstlen, const wchar_t* src, unsigned srclen) FL_OVERRIDE; - int utf8locale() FL_OVERRIDE; - unsigned utf8to_mb(const char *src, unsigned srclen, char *dst, unsigned dstlen) FL_OVERRIDE; - unsigned utf8from_mb(char *dst, unsigned dstlen, const char *src, unsigned srclen) FL_OVERRIDE; - - int clocale_vprintf(FILE *output, const char *format, va_list args) FL_OVERRIDE; - int clocale_vsnprintf(char *output, size_t output_size, const char *format, va_list args) FL_OVERRIDE; - int clocale_vsscanf(const char *input, const char *format, va_list args) FL_OVERRIDE; - int filename_list(const char *d, dirent ***list, - int (*sort)(struct dirent **, struct dirent **), - char *errmsg=NULL, int errmsg_sz=0) FL_OVERRIDE; - int filename_expand(char *to,int tolen, const char *from) FL_OVERRIDE; - int filename_relative(char *to, int tolen, const char *from, const char *base) FL_OVERRIDE; - int filename_absolute(char *to, int tolen, const char *from, const char *base) FL_OVERRIDE; - int filename_isdir(const char *n) FL_OVERRIDE; - int filename_isdir_quick(const char *n) FL_OVERRIDE; - const char *filename_ext(const char *buf) FL_OVERRIDE; - int open_uri(const char *uri, char *msg, int msglen) FL_OVERRIDE; - int use_recent_tooltip_fix() FL_OVERRIDE {return 1;} - int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon) FL_OVERRIDE; - int file_browser_load_directory(const char *directory, char *filename, size_t name_size, - dirent ***pfiles, Fl_File_Sort_F *sort, - char *errmsg=NULL, int errmsg_sz=0) FL_OVERRIDE; - void newUUID(char *uuidBuffer) FL_OVERRIDE; - char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor, - const char *application) FL_OVERRIDE; - void *load(const char *filename) FL_OVERRIDE; - void png_extra_rgba_processing(unsigned char *array, int w, int h) FL_OVERRIDE; - const char *next_dir_sep(const char *start) FL_OVERRIDE; - // these 3 are implemented in Fl_lock.cxx - void awake(void*) FL_OVERRIDE; - int lock() FL_OVERRIDE; - void unlock() FL_OVERRIDE; - // this one is implemented in Fl_win32.cxx - void* thread_message() FL_OVERRIDE; - int file_type(const char *filename) FL_OVERRIDE; - const char *home_directory_name() FL_OVERRIDE; - const char *filesystems_label() FL_OVERRIDE { return "My Computer"; } - int backslash_as_slash() FL_OVERRIDE {return 1;} - int colon_is_drive() FL_OVERRIDE {return 1;} - int case_insensitive_filenames() FL_OVERRIDE {return 1;} - // this one is implemented in Fl_win32.cxx - const char *filename_name(const char *buf) FL_OVERRIDE; - void add_fd(int fd, int when, Fl_FD_Handler cb, void* = 0) FL_OVERRIDE; - void add_fd(int fd, Fl_FD_Handler cb, void* = 0) FL_OVERRIDE; - void remove_fd(int, int when) FL_OVERRIDE; - void remove_fd(int) FL_OVERRIDE; - void gettime(time_t *sec, int *usec) FL_OVERRIDE; - char* strdup(const char *s) FL_OVERRIDE { return ::_strdup(s); } - void lock_ring() FL_OVERRIDE; - void unlock_ring() FL_OVERRIDE; - double wait(double time_to_wait) FL_OVERRIDE; - int ready() FL_OVERRIDE; - int close_fd(int fd) FL_OVERRIDE; -}; - -#endif // FL_WINAPI_SYSTEM_DRIVER_H diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx deleted file mode 100644 index 9eab455df..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx +++ /dev/null @@ -1,1134 +0,0 @@ -// -// Definition of Windows system driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include -#include "Fl_WinAPI_System_Driver.H" -#include -#include -#include -#include -#include -#include "../../flstring.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// We must define _WIN32_IE at least to 0x0500 before inclusion of 'shlobj.h' to enable -// the declaration of SHGFP_TYPE_CURRENT for some older versions of MinGW, notably -// header versions 5.3.0 and earlier, whereas 5.4.2 seems to define _WIN32_IE as needed. -#if !(defined _WIN32_IE) || (_WIN32_IE < 0x0500) -# undef _WIN32_IE -# define _WIN32_IE 0x0500 -#endif /* _WIN32_WINNT checks */ - -#include - -// function pointer for the UuidCreate Function -// RPC_STATUS RPC_ENTRY UuidCreate(UUID __RPC_FAR *Uuid); -typedef RPC_STATUS (WINAPI *uuid_func)(UUID __RPC_FAR *Uuid); - -// Apparently Borland C++ defines DIRECTORY in , which -// interferes with the Fl_File_Icon enumeration of the same name. -# ifdef DIRECTORY -# undef DIRECTORY -# endif // DIRECTORY - -#ifdef __CYGWIN__ -# include -#endif - -// Optional helper function to debug Fl_WinAPI_System_Driver::home_directory_name() -#ifndef DEBUG_HOME_DIRECTORY_NAME -#define DEBUG_HOME_DIRECTORY_NAME 0 -#endif -#if DEBUG_HOME_DIRECTORY_NAME -static void print_env(const char *ev) { - const char *val = getenv(ev); - printf("%-30.30s = \"%s\"\n", ev, val ? val : ""); - fflush(stdout); -} -#endif // DEBUG_HOME_DIRECTORY_NAME - -static inline int isdirsep(char c) { return c == '/' || c == '\\'; } - -static wchar_t *mbwbuf = NULL; -static wchar_t *wbuf = NULL; -static wchar_t *wbuf1 = NULL; - -extern "C" { - int fl_scandir(const char *dirname, struct dirent ***namelist, - int (*select)(struct dirent *), - int (*compar)(struct dirent **, struct dirent **), - char *errmsg, int errmsg_len); -} - -/* - Convert UTF-8 string to Windows wide character encoding (UTF-16). - - This helper function is used throughout this file to convert UTF-8 - strings to Windows specific UTF-16 encoding for filenames, paths, or - other strings to be used by system functions. - - The input string can be a null-terminated string or its length can be - provided by the optional argument 'lg'. If 'lg' is omitted or less than 0 - (default = -1) the string length is determined with strlen(), otherwise - 'lg' takes precedence. Zero (0) is a valid string length (an empty string). - - The argument 'wbuf' must have been initialized with NULL or a previous - call to malloc() or realloc(). - - If the converted string doesn't fit into the allocated size of 'wbuf' or if - 'wbuf' is NULL a new buffer is allocated with realloc(). Hence the pointer - 'wbuf' can be shared among multiple calls to this function if it has been - initialized with NULL (or malloc or realloc) before the first call. - - The return value is either the old value of 'wbuf' (if the string fits) - or a pointer to the (re)allocated buffer. - - Pseudo doxygen docs (static function intentionally not documented): - - param[in] utf8 input string (UTF-8) - param[in,out] wbuf in: pointer to output string buffer or NULL - out: new string (the pointer may be changed) - param[in] lg optional: input string length (default = -1) - - returns pointer to string buffer -*/ -static wchar_t *utf8_to_wchar(const char *utf8, wchar_t *&wbuf, int lg = -1) { - unsigned len = (lg >= 0) ? (unsigned)lg : (unsigned)strlen(utf8); - unsigned wn = fl_utf8toUtf16(utf8, len, NULL, 0) + 1; // Query length - wbuf = (wchar_t *)realloc(wbuf, sizeof(wchar_t) * wn); - wn = fl_utf8toUtf16(utf8, len, (unsigned short *)wbuf, wn); // Convert string - wbuf[wn] = 0; - return wbuf; -} - -/* - Convert a Windows wide character (UTF-16) string to UTF-8 encoding. - - This helper function is used throughout this file to convert Windows - wide character strings as returned by system functions to UTF-8 - encoding for internal usage. - - The argument 'utf8' must have been initialized with NULL or a previous - call to malloc() or realloc(). - - If the converted string doesn't fit into the allocated size of 'utf8' or if - 'utf8' is NULL a new buffer is allocated with realloc(). Hence the pointer - 'utf8' can be shared among multiple calls to this function if it has been - initialized with NULL (or malloc or realloc) before the first call. - Ideally every call to this function has its own static pointer though. - - The return value is either the old value of 'utf8' (if the string fits) - or a pointer at the (re)allocated buffer. - - Pseudo doxygen docs (static function intentionally not documented): - - param[in] wstr input string (wide character, UTF-16) - param[in,out] utf8 in: pointer to output string buffer - out: new string (pointer may be changed) - - returns pointer to string buffer -*/ -static char *wchar_to_utf8(const wchar_t *wstr, char *&utf8) { - unsigned len = (unsigned)wcslen(wstr); - unsigned wn = fl_utf8fromwc(NULL, 0, wstr, len) + 1; // query length - utf8 = (char *)realloc(utf8, wn); - wn = fl_utf8fromwc(utf8, wn, wstr, len); // convert string - utf8[wn] = 0; - return utf8; -} - -void Fl_WinAPI_System_Driver::warning(const char *format, va_list args) { - // Show nothing for warnings under Windows... -} - -void Fl_WinAPI_System_Driver::error(const char *format, va_list args) { - char buf[1024]; - vsnprintf(buf, 1024, format, args); - MessageBox(0, buf, "Error", MB_ICONEXCLAMATION | MB_SYSTEMMODAL); -} - -void Fl_WinAPI_System_Driver::fatal(const char *format, va_list args) { - char buf[1024]; - vsnprintf(buf, 1024, format, args); - MessageBox(0, buf, "Error", MB_ICONSTOP | MB_SYSTEMMODAL); - ::exit(1); -} - -char *Fl_WinAPI_System_Driver::utf2mbcs(const char *utf8) { - static char *buf = NULL; - if (!utf8) return NULL; - - unsigned len = (unsigned)strlen(utf8); - - unsigned wn = fl_utf8toUtf16(utf8, len, NULL, 0) + 7; // Query length - mbwbuf = (wchar_t *)realloc(mbwbuf, sizeof(wchar_t) * wn); - len = fl_utf8toUtf16(utf8, len, (unsigned short *)mbwbuf, wn); // Convert string - mbwbuf[len] = 0; - - buf = (char*)realloc(buf, len * 6 + 2); - len = (unsigned)wcstombs(buf, mbwbuf, len * 6); - buf[len] = 0; - buf[len+1] = 0; // in case the result is a UTF-16 string - return buf; -} - -char *Fl_WinAPI_System_Driver::getenv(const char *var) { - static char *buf = NULL; - wchar_t *ret = _wgetenv(utf8_to_wchar(var, wbuf)); - if (!ret) return NULL; - return wchar_to_utf8(ret, buf); -} - -int Fl_WinAPI_System_Driver::putenv(const char *var) { - unsigned len = (unsigned)strlen(var); - unsigned wn = fl_utf8toUtf16(var, len, NULL, 0) + 1; // Query length - wchar_t *wbuf = (wchar_t *)malloc(sizeof(wchar_t) * wn); - wn = fl_utf8toUtf16(var, len, (unsigned short *)wbuf, wn); - wbuf[wn] = 0; - int ret = _wputenv(wbuf); - free(wbuf); - return ret; -} - -int Fl_WinAPI_System_Driver::open(const char *fnam, int oflags, int pmode) { - utf8_to_wchar(fnam, wbuf); - if (pmode == -1) return _wopen(wbuf, oflags); - else return _wopen(wbuf, oflags, pmode); -} - -int Fl_WinAPI_System_Driver::open_ext(const char *fnam, int binary, int oflags, int pmode) { - if (oflags == 0) oflags = _O_RDONLY; - oflags |= (binary ? _O_BINARY : _O_TEXT); - return this->open(fnam, oflags, pmode); -} - -FILE *Fl_WinAPI_System_Driver::fopen(const char *fnam, const char *mode) { - utf8_to_wchar(fnam, wbuf); - utf8_to_wchar(mode, wbuf1); - return _wfopen(wbuf, wbuf1); -} - -int Fl_WinAPI_System_Driver::system(const char *cmd) { - return _wsystem(utf8_to_wchar(cmd, wbuf)); -} - -int Fl_WinAPI_System_Driver::execvp(const char *file, char *const *argv) { - int n = 0; - while (argv[n]) n++; // count args - wchar_t **ar = (wchar_t **)calloc(sizeof(wchar_t *), n + 1); - // convert arguments first; trailing NULL provided by calloc() - for (int i = 0; i < n; i++) - ar[i] = utf8_to_wchar(argv[i], ar[i]); // alloc and assign - // convert executable file and execute it ... - utf8_to_wchar(file, wbuf); - _wexecvp(wbuf, ar); // STR #3040 - // clean up (reached only if _wexecvp() failed) - for (int i = 0; i < n; i++) - free(ar[i]); - free(ar); - return -1; // STR #3040 -} - -int Fl_WinAPI_System_Driver::chmod(const char *fnam, int mode) { - return _wchmod(utf8_to_wchar(fnam, wbuf), mode); -} - -int Fl_WinAPI_System_Driver::access(const char *fnam, int mode) { - return _waccess(utf8_to_wchar(fnam, wbuf), mode); -} - -int Fl_WinAPI_System_Driver::flstat(const char *fnam, struct stat *b) { - - // remove trailing '/' or '\' - unsigned len = (unsigned)strlen(fnam); - if (len > 0 && (fnam[len-1] == '/' || fnam[len-1] == '\\')) - len--; - // convert filename and execute _wstat() - return _wstat(utf8_to_wchar(fnam, wbuf, len), (struct _stat *)b); -} - -char *Fl_WinAPI_System_Driver::getcwd(char *buf, int len) { - - static wchar_t *wbuf = NULL; - wbuf = (wchar_t *)realloc(wbuf, sizeof(wchar_t) * (len + 1)); - wchar_t *ret = _wgetcwd(wbuf, len); - if (!ret) return NULL; - - unsigned dstlen = (unsigned)len; - len = (int)wcslen(wbuf); - dstlen = fl_utf8fromwc(buf, dstlen, wbuf, (unsigned)len); - buf[dstlen] = 0; - return buf; -} - -int Fl_WinAPI_System_Driver::chdir(const char *path) { - return _wchdir(utf8_to_wchar(path, wbuf)); -} - -int Fl_WinAPI_System_Driver::unlink(const char *fnam) { - return _wunlink(utf8_to_wchar(fnam, wbuf)); -} - -int Fl_WinAPI_System_Driver::mkdir(const char *fnam, int mode) { - return _wmkdir(utf8_to_wchar(fnam, wbuf)); -} - -int Fl_WinAPI_System_Driver::rmdir(const char *fnam) { - return _wrmdir(utf8_to_wchar(fnam, wbuf)); -} - -int Fl_WinAPI_System_Driver::rename(const char *fnam, const char *newnam) { - utf8_to_wchar(fnam, wbuf); - utf8_to_wchar(newnam, wbuf1); - return _wrename(wbuf, wbuf1); -} - -// See Fl::args_to_utf8() -int Fl_WinAPI_System_Driver::args_to_utf8(int argc, char ** &argv) { - int i; - - // Convert the command line arguments to UTF-8 - LPWSTR *wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc); - argv = (char **)malloc((argc + 1) * sizeof(char *)); - for (i = 0; i < argc; i++) { - // find the required size of the buffer - int u8size = WideCharToMultiByte(CP_UTF8, // CodePage - 0, // dwFlags - wideArgv[i], // lpWideCharStr - -1, // cchWideChar - NULL, // lpMultiByteStr - 0, // cbMultiByte - NULL, // lpDefaultChar - NULL); // lpUsedDefaultChar - if (u8size > 0) { - char *strbuf = (char*)::malloc(u8size); - int ret = WideCharToMultiByte(CP_UTF8, // CodePage - 0, // dwFlags - wideArgv[i], // lpWideCharStr - -1, // cchWideChar - strbuf, // lpMultiByteStr - u8size, // cbMultiByte - NULL, // lpDefaultChar - NULL); // lpUsedDefaultChar - - if (ret) { - argv[i] = strbuf; - } else { - argv[i] = _strdup(""); - ::free(strbuf); - } - } else { - argv[i] = _strdup(""); - } - } - argv[argc] = NULL; // required NULL pointer at end of list - - // Free the wide character string array - LocalFree(wideArgv); - - // Note: the allocated memory or argv[] will not be free'd by the system - // on exit. This does not constitute a memory leak. - - return argc; -} - - -// Two Windows-specific functions fl_utf8_to_locale() and fl_locale_to_utf8() -// from file fl_utf8.cxx are put here for API compatibility - -static char *buf = NULL; -static int buf_len = 0; -static unsigned short *wbufa = NULL; -unsigned int fl_codepage = 0; - - -// FIXME: This should *maybe* return 'const char *' instead of 'char *' -char *fl_utf8_to_locale(const char *s, int len, UINT codepage) -{ - if (!s) return (char *)""; - int l = 0; - unsigned wn = fl_utf8toUtf16(s, len, NULL, 0); // Query length - wn = wn * 2 + 1; - if (wn >= (unsigned)buf_len) { - buf_len = wn; - buf = (char*) realloc(buf, buf_len); - wbufa = (unsigned short*) realloc(wbufa, buf_len * sizeof(short)); - } - if (codepage < 1) codepage = fl_codepage; - l = fl_utf8toUtf16(s, len, wbufa, wn); // Convert string - wbufa[l] = 0; - buf[l] = 0; - l = WideCharToMultiByte(codepage, 0, (WCHAR*)wbufa, l, buf, buf_len, NULL, NULL); - if (l < 0) l = 0; - buf[l] = 0; - return buf; -} - -// FIXME: This should maybe return 'const char *' instead of 'char *' -char *fl_locale_to_utf8(const char *s, int len, UINT codepage) -{ - if (!s) return (char *)""; - int l = 0; - if (buf_len < len * 5 + 1) { - buf_len = len * 5 + 1; - buf = (char*) realloc(buf, buf_len); - wbufa = (unsigned short*) realloc(wbufa, buf_len * sizeof(short)); - } - if (codepage < 1) codepage = fl_codepage; - buf[l] = 0; - - l = MultiByteToWideChar(codepage, 0, s, len, (WCHAR*)wbufa, buf_len); - if (l < 0) l = 0; - wbufa[l] = 0; - l = fl_utf8fromwc(buf, buf_len, (wchar_t*)wbufa, l); - buf[l] = 0; - return buf; -} - -/////////////////////////////////// - -unsigned Fl_WinAPI_System_Driver::utf8towc(const char *src, unsigned srclen, wchar_t *dst, unsigned dstlen) { - return fl_utf8toUtf16(src, srclen, (unsigned short*)dst, dstlen); -} - -unsigned Fl_WinAPI_System_Driver::utf8fromwc(char *dst, unsigned dstlen, const wchar_t *src, unsigned srclen) { - unsigned i = 0; - unsigned count = 0; - if (dstlen) for (;;) { - unsigned ucs; - if (i >= srclen) { - dst[count] = 0; - return count; - } - ucs = src[i++]; - if (ucs < 0x80U) { - dst[count++] = ucs; - if (count >= dstlen) {dst[count-1] = 0; break;} - } else if (ucs < 0x800U) { /* 2 bytes */ - if (count+2 >= dstlen) {dst[count] = 0; count += 2; break;} - dst[count++] = 0xc0 | (ucs >> 6); - dst[count++] = 0x80 | (ucs & 0x3F); - } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen && - src[i] >= 0xdc00 && src[i] <= 0xdfff) { - /* surrogate pair */ - unsigned ucs2 = src[i++]; - ucs = 0x10000U + ((ucs&0x3ff)<<10) + (ucs2&0x3ff); - /* all surrogate pairs turn into 4-byte UTF-8 */ - if (count+4 >= dstlen) {dst[count] = 0; count += 4; break;} - dst[count++] = 0xf0 | (ucs >> 18); - dst[count++] = 0x80 | ((ucs >> 12) & 0x3F); - dst[count++] = 0x80 | ((ucs >> 6) & 0x3F); - dst[count++] = 0x80 | (ucs & 0x3F); - } else { - /* all others are 3 bytes: */ - if (count+3 >= dstlen) {dst[count] = 0; count += 3; break;} - dst[count++] = 0xe0 | (ucs >> 12); - dst[count++] = 0x80 | ((ucs >> 6) & 0x3F); - dst[count++] = 0x80 | (ucs & 0x3F); - } - } - /* we filled dst, measure the rest: */ - while (i < srclen) { - unsigned ucs = src[i++]; - if (ucs < 0x80U) { - count++; - } else if (ucs < 0x800U) { /* 2 bytes */ - count += 2; - } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen-1 && - src[i+1] >= 0xdc00 && src[i+1] <= 0xdfff) { - /* surrogate pair */ - ++i; - count += 4; - } else { - count += 3; - } - } - return count; -} - -int Fl_WinAPI_System_Driver::utf8locale() -{ - static int ret = (GetACP() == CP_UTF8); - return ret; -} - -unsigned Fl_WinAPI_System_Driver::utf8to_mb(const char *src, unsigned srclen, char *dst, unsigned dstlen) { - wchar_t lbuf[1024]; - wchar_t *buf = lbuf; - unsigned length = fl_utf8towc(src, srclen, buf, 1024); - unsigned ret; - if (length >= 1024) { - buf = (wchar_t*)(malloc((length+1)*sizeof(wchar_t))); - fl_utf8towc(src, srclen, buf, length+1); - } - if (dstlen) { - // apparently this does not null-terminate, even though msdn documentation claims it does: - ret = - WideCharToMultiByte(GetACP(), 0, buf, length, dst, dstlen, 0, 0); - dst[ret] = 0; - } - // if it overflows or measuring length, get the actual length: - if (dstlen==0 || ret >= dstlen-1) - ret = WideCharToMultiByte(GetACP(), 0, buf, length, 0, 0, 0, 0); - if (buf != lbuf) free(buf); - return ret; -} - -unsigned Fl_WinAPI_System_Driver::utf8from_mb(char *dst, unsigned dstlen, const char *src, unsigned srclen) { - wchar_t lbuf[1024]; - wchar_t *buf = lbuf; - unsigned length; - unsigned ret; - length = MultiByteToWideChar(GetACP(), 0, src, srclen, buf, 1024); - if ((length == 0)&&(GetLastError()==ERROR_INSUFFICIENT_BUFFER)) { - length = MultiByteToWideChar(GetACP(), 0, src, srclen, 0, 0); - buf = (wchar_t*)(malloc(length*sizeof(wchar_t))); - MultiByteToWideChar(GetACP(), 0, src, srclen, buf, length); - } - ret = fl_utf8fromwc(dst, dstlen, buf, length); - if (buf != lbuf) free((void*)buf); - return ret; -} - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/) -static _locale_t c_locale = NULL; -#endif - -int Fl_WinAPI_System_Driver::clocale_vprintf(FILE *output, const char *format, va_list args) { -#if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/) - if (!c_locale) - c_locale = _create_locale(LC_NUMERIC, "C"); - int retval = _vfprintf_l(output, format, c_locale, args); -#else - char *saved_locale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); - int retval = vfprintf(output, format, args); - setlocale(LC_NUMERIC, saved_locale); -#endif - return retval; -} - -int Fl_WinAPI_System_Driver::clocale_vsnprintf(char *output, size_t output_size, const char *format, va_list args) { -#if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/) - if (!c_locale) - c_locale = _create_locale(LC_NUMERIC, "C"); - int retval = _vsnprintf_l(output, output_size, format, c_locale, args); -#else - char *saved_locale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); - int retval = vsnprintf(output, output_size, format, args); - setlocale(LC_NUMERIC, saved_locale); -#endif - return retval; -} - -int Fl_WinAPI_System_Driver::clocale_vsscanf(const char *input, const char *format, va_list args) { - char *saved_locale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); - int retval = vsscanf(input, format, args); - setlocale(LC_NUMERIC, saved_locale); - return retval; -} - - -int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list, - int (*sort)(struct dirent **, struct dirent **), - char *errmsg, int errmsg_sz) { - // For Windows we have a special scandir implementation that uses - // the Win32 "wide" functions for lookup, avoiding the code page mess - // entirely. It also fixes up the trailing '/'. - return fl_scandir(d, list, 0, sort, errmsg, errmsg_sz); -} - -int Fl_WinAPI_System_Driver::filename_expand(char *to, int tolen, const char *from) { - char *temp = new char[tolen]; - strlcpy(temp,from, tolen); - char *start = temp; - char *end = temp+strlen(temp); - int ret = 0; - for (char *a=temp; a= tolen) end += tolen - (end+1-e+t); - memmove(a+t, e, end+1-e); - end = a+t+(end-e); - *end = '\0'; - memcpy(a, value, t); - ret++; - } else { - a = e+1; - if (*e == '\\') {*e = '/'; ret++;} // ha ha! - } - } - strlcpy(to, start, tolen); - delete[] temp; - return ret; -} - -int // O - 0 if no change, 1 if changed -Fl_WinAPI_System_Driver::filename_relative(char *to, // O - Relative filename - int tolen, // I - Size of "to" buffer - const char *dest_dir, // I - Absolute filename - const char *base_dir) // I - Find path relative to this path -{ - // Find the relative path from base_dir to dest_dir. - // Both paths must be absolute and well formed (contain no /../ and /./ segments). - - // return if any of the pointers is NULL - if (!to || !dest_dir || !base_dir) { - return 0; - } - - // if there is a drive letter, make sure both paths use the same drive - if ( (unsigned)base_dir[0] < 128 && isalpha(base_dir[0]) && base_dir[1] == ':' - && (unsigned)dest_dir[0] < 128 && isalpha(dest_dir[0]) && dest_dir[1] == ':') { - if (tolower(base_dir[0]) != tolower(dest_dir[0])) { - strlcpy(to, dest_dir, tolen); - return 0; - } - // same drive, so skip to the start of the path - base_dir += 2; - dest_dir += 2; - } - - // return if `base_dir` or `dest_dir` is not an absolute path - if (!isdirsep(*base_dir) || !isdirsep(*dest_dir)) { - strlcpy(to, dest_dir, tolen); - return 0; - } - - const char *base_i = base_dir; // iterator through the base directory string - const char *base_s = base_dir; // pointer to the last dir separator found - const char *dest_i = dest_dir; // iterator through the destination directory - const char *dest_s = dest_dir; // pointer to the last dir separator found - - // compare both path names until we find a difference - for (;;) { -#if 0 // case sensitive - base_i++; - dest_i++; - char b = *base_i, d = *dest_i; -#else // case insensitive - base_i += fl_utf8len1(*base_i); - int b = fl_tolower(fl_utf8decode(base_i, NULL, NULL)); - dest_i += fl_utf8len1(*dest_i); - int d = fl_tolower(fl_utf8decode(dest_i, NULL, NULL)); -#endif - int b0 = (b == 0) || (isdirsep(b)); - int d0 = (d == 0) || (isdirsep(d)); - if (b0 && d0) { - base_s = base_i; - dest_s = dest_i; - } - if (b == 0 || d == 0) - break; - if (b != d) - break; - } - // base_s and dest_s point at the last separator we found - // base_i and dest_i point at the first character that differs - - // test for the exact same string and return "." if so - if ( (base_i[0] == 0 || (isdirsep(base_i[0]) && base_i[1] == 0)) - && (dest_i[0] == 0 || (isdirsep(dest_i[0]) && dest_i[1] == 0))) { - strlcpy(to, ".", tolen); - return 0; - } - - // prepare the destination buffer - to[0] = '\0'; - to[tolen - 1] = '\0'; - - // count the directory segments remaining in `base_dir` - int n_up = 0; - for (;;) { - char b = *base_s++; - if (b == 0) - break; - if (isdirsep(b) && *base_s) - n_up++; - } - - // now add a "previous dir" sequence for every following slash in the cwd - if (n_up > 0) - strlcat(to, "..", tolen); - for (; n_up > 1; --n_up) - strlcat(to, "/..", tolen); - - // finally add the differing path from "from" - if (*dest_s) { - if (n_up) - strlcat(to, "/", tolen); - strlcat(to, dest_s + 1, tolen); - } - - return 1; -} - -int Fl_WinAPI_System_Driver::filename_absolute(char *to, int tolen, const char *from, const char *base) { - if (isdirsep(*from) || *from == '|' || from[1]==':' || !base) { - strlcpy(to, from, tolen); - return 0; - } - char *a; - char *temp = new char[tolen]; - const char *start = from; - strlcpy(temp, base, tolen); - for (a = temp; *a; a++) if (*a=='\\') *a = '/'; // ha ha - /* remove trailing '/' in current working directory */ - if (isdirsep(*(a-1))) a--; - /* remove intermediate . and .. names: */ - while (*start == '.') { - if (start[1]=='.' && (isdirsep(start[2]) || start[2]==0) ) { - // found "..", remove the last directory segment form cwd - char *b; - for (b = a-1; b >= temp && !isdirsep(*b); b--) {/*empty*/} - if (b < temp) break; - a = b; - if (start[2] == 0) - start += 2; - else - start += 3; - } else if (isdirsep(start[1])) { - // found "./" in path, just skip it - start += 2; - } else if (!start[1]) { - // found "." at end of path, just skip it - start ++; - break; - } else - break; - } - *a++ = '/'; - strlcpy(a,start,tolen - (a - temp)); - strlcpy(to, temp, tolen); - delete[] temp; - return 1; -} - -int Fl_WinAPI_System_Driver::filename_isdir(const char *n) { - char fn[4]; // used for drive letter only: "X:/" - int length = (int)strlen(n); - // Strip trailing slash from name... - if (length > 0 && isdirsep(n[length - 1])) - length --; - if (length < 1) - return 0; - - // This workaround brought to you by the fine folks at Microsoft! - // (read lots of sarcasm in that...) - - if (length == 2 && isalpha(n[0]) && n[1] == ':') { // trailing '/' already "removed" - // Always use "X:/" for drive letters - fn[0] = n[0]; - strcpy(fn + 1, ":/"); - n = fn; - length = 3; - } - - // convert filename to wide chars using *length* - utf8_to_wchar(n, wbuf, length); - - DWORD fa = GetFileAttributesW(wbuf); - return (fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY); -} - -int Fl_WinAPI_System_Driver::filename_isdir_quick(const char *n) { - // Do a quick optimization for filenames with a trailing slash... - if (*n && isdirsep(n[strlen(n) - 1])) return 1; - return filename_isdir(n); -} - -const char *Fl_WinAPI_System_Driver::filename_ext(const char *buf) { - const char *q = 0; - const char *p = buf; - for (p = buf; *p; p++) { - if (isdirsep(*p) ) q = 0; - else if (*p == '.') q = p; - } - return q ? q : p; -} - -int Fl_WinAPI_System_Driver::open_uri(const char *uri, char *msg, int msglen) { - if (msg) snprintf(msg, msglen, "open %s", uri); - return (int)(ShellExecute(HWND_DESKTOP, "open", uri, NULL, NULL, SW_SHOW) > (void *)32); -} - -int Fl_WinAPI_System_Driver::file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, - int lname, Fl_File_Icon *icon) { - int num_files = 0; -# ifdef __CYGWIN__ - // - // Cygwin provides an implementation of setmntent() to get the list - // of available drives... - // - FILE *m = setmntent("/-not-used-", "r"); - struct mntent *p; - while ((p = getmntent (m)) != NULL) { - browser->add(p->mnt_dir, icon); - num_files ++; - } - endmntent(m); -# else - // - // Normal Windows code uses drive bits... - // - DWORD drives; // Drive available bits - drives = GetLogicalDrives(); - for (int i = 'A'; i <= 'Z'; i ++, drives >>= 1) { - if (drives & 1) { - snprintf(filename, lname, "%c:/", i); - if (i < 'C') // see also: GetDriveType and GetVolumeInformation in Windows - browser->add(filename, icon); - else - browser->add(filename, icon); - num_files ++; - } - } -# endif // __CYGWIN__ - return num_files; -} - -int Fl_WinAPI_System_Driver::file_browser_load_directory(const char *directory, char *filename, - size_t name_size, dirent ***pfiles, - Fl_File_Sort_F *sort, - char *errmsg, int errmsg_sz) -{ - strlcpy(filename, directory, name_size); - int i = (int) (strlen(filename) - 1); - if (i == 2 && filename[1] == ':' && - (filename[2] == '/' || filename[2] == '\\')) - filename[2] = '/'; - else if (filename[i] != '/' && filename[i] != '\\') - strlcat(filename, "/", name_size); - return filename_list(filename, pfiles, sort, errmsg, errmsg_sz); -} - -void Fl_WinAPI_System_Driver::newUUID(char *uuidBuffer) -{ - // First try and use the win API function UuidCreate(), but if that is not - // available, fall back to making something up from scratch. - // We do not want to link against the Rpcrt4.dll, as we will rarely use it, - // so we load the DLL dynamically, if it is available, and work from there. - static HMODULE hMod = NULL; - UUID ud; - UUID *pu = &ud; - int got_uuid = 0; - - if (!hMod) { // first time in? - hMod = LoadLibrary("Rpcrt4.dll"); - } - - if (hMod) { // do we have a usable handle to Rpcrt4.dll? - uuid_func uuid_crt = (uuid_func)GetProcAddress(hMod, "UuidCreate"); - if (uuid_crt != NULL) { - RPC_STATUS rpc_res = uuid_crt(pu); - if ( // is the return status OK for our needs? - (rpc_res == RPC_S_OK) || // all is well - (rpc_res == RPC_S_UUID_LOCAL_ONLY) || // only unique to this machine - (rpc_res == RPC_S_UUID_NO_ADDRESS) // probably only locally unique - ) { - got_uuid = -1; - snprintf(uuidBuffer, 36+1, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", - pu->Data1, pu->Data2, pu->Data3, pu->Data4[0], pu->Data4[1], - pu->Data4[2], pu->Data4[3], pu->Data4[4], - pu->Data4[5], pu->Data4[6], pu->Data4[7]); - } - } - } - if (got_uuid == 0) { // did not make a UUID - use fallback logic - unsigned char b[16]; - time_t t = time(0); // first 4 byte - b[0] = (unsigned char)t; - b[1] = (unsigned char)(t>>8); - b[2] = (unsigned char)(t>>16); - b[3] = (unsigned char)(t>>24); - int r = rand(); // four more bytes - b[4] = (unsigned char)r; - b[5] = (unsigned char)(r>>8); - b[6] = (unsigned char)(r>>16); - b[7] = (unsigned char)(r>>24); - // Now we try to find 4 more "random" bytes. We extract the - // lower 4 bytes from the address of t - it is created on the - // stack so *might* be in a different place each time... - // This is now done via a union to make it compile OK on 64-bit systems. - union { void *pv; unsigned char a[sizeof(void*)]; } v; - v.pv = (void *)(&t); - // NOTE: This assume that all WinXX systems are little-endian - b[8] = v.a[0]; - b[9] = v.a[1]; - b[10] = v.a[2]; - b[11] = v.a[3]; - TCHAR name[MAX_COMPUTERNAME_LENGTH + 1]; // only used to make last four bytes - DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1; - // GetComputerName() does not depend on any extra libs, and returns something - // analogous to gethostname() - GetComputerName(name, &nSize); - // use the first 4 TCHAR's of the name to create the last 4 bytes of our UUID - for (int ii = 0; ii < 4; ii++) { - b[12 + ii] = (unsigned char)name[ii]; - } - snprintf(uuidBuffer, 36+1, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", - b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], - b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]); - } -} - -/* - Note: `prefs` can be NULL! - */ -char *Fl_WinAPI_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_Preferences::Root root, const char *vendor, - const char *application) -{ - static char *filename = 0L; - // make enough room for a UTF-16 pathname - if (!filename) filename = (char*)::malloc(2 * FL_PATH_MAX); - HRESULT res; - - // https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw - - int appdata = CSIDL_APPDATA; // assume user preferences - if ((root & Fl_Preferences::ROOT_MASK) == Fl_Preferences::SYSTEM) - appdata = CSIDL_COMMON_APPDATA; // use system preferences - - res = SHGetFolderPathW(NULL, // hwnd: Reserved! - appdata, // csidl: User or common Application Data (Roaming) - NULL, // hToken (unused) - SHGFP_TYPE_CURRENT, // dwFlags: use current, potentially redirected path - (LPWSTR)filename); // out: filename in Windows wide string encoding - if (res != S_OK) { - // don't write data into some arbitrary directory! Just return NULL. - return 0L; - } - - // convert the path from Windows wide character (UTF-16) to UTF-8 - // FIXME: can this be simplified? Don't allocate/copy/move/free more than necessary! - char *buf = NULL; - wchar_to_utf8((wchar_t *)filename, buf); // allocates buf for conversion - strcpy(filename, buf); - free(buf); - - // Make sure that the parameters are not NULL - if ( (vendor==0L) || (vendor[0]==0) ) - vendor = "unknown"; - if ( (application==0L) || (application[0]==0) ) - application = "unknown"; - - // append vendor, application, and ".prefs", and convert '\' to '/' - snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename), - "/%s/%s.prefs", vendor, application); - for (char *s = filename; *s; s++) if (*s == '\\') *s = '/'; - return filename; -} - -void *Fl_WinAPI_System_Driver::load(const char *filename) { - return LoadLibraryW(utf8_to_wchar(filename, wbuf)); -} - -void Fl_WinAPI_System_Driver::png_extra_rgba_processing(unsigned char *ptr, int w, int h) -{ - // Some Windows graphics drivers don't honor transparency when RGB == white - // Convert RGB to 0 when alpha == 0... - for (int i = w * h; i > 0; i --, ptr += 4) { - if (!ptr[3]) ptr[0] = ptr[1] = ptr[2] = 0; - } -} - -const char *Fl_WinAPI_System_Driver::next_dir_sep(const char *start) -{ - const char *p = strchr(start, '/'); - if (!p) p = strchr(start, '\\'); - return p; -} - -int Fl_WinAPI_System_Driver::file_type(const char *filename) -{ - int filetype; - if (filename[strlen(filename) - 1] == '/') - filetype = Fl_File_Icon::DIRECTORY; - else if (filename_isdir(filename)) - filetype = Fl_File_Icon::DIRECTORY; - else - filetype = Fl_File_Icon::PLAIN; - return filetype; -} - -// Note: the result is cached in a static variable -const char *Fl_WinAPI_System_Driver::home_directory_name() -{ - static std::string home; - if (!home.empty()) - return home.c_str(); - -#if (DEBUG_HOME_DIRECTORY_NAME) - print_env("HOMEDRIVE"); - print_env("HOMEPATH"); - print_env("UserProfile"); - print_env("HOME"); -#endif - - // Implement various ways to retrieve the HOME path. - // Note, from `man getenv`: - // "The implementation of getenv() is not required to be reentrant. - // The string pointed to by the return value of getenv() may be statically - // allocated, and can be modified by a subsequent call to getenv()...". - // Tests show that this is the case in some MinGW implementations. - - if (home.empty()) { - const char *home_drive = getenv("HOMEDRIVE"); - if (home_drive) { - home = home_drive; // copy *before* calling getenv() again, see above - const char *home_path = getenv("HOMEPATH"); - if (home_path) { - home.append(home_path); - } else { - home.clear(); // reset - } // home_path - } // home_drive - } // empty() - - if (home.empty()) { - const char *h = getenv("UserProfile"); - if (h) - home = h; - } - - if (home.empty()) { - const char *h = getenv("HOME"); - if (h) - home = h; - } - if (home.empty()) { - home = "~/"; // last resort - } - // Make path canonical. - for (char& c : home) { - if (c == '\\') - c = '/'; - } -#if (DEBUG_HOME_DIRECTORY_NAME) - printf("home_directory_name() returns \"%s\"\n", home.c_str()); - fflush(stdout); -#endif - return home.c_str(); -} - -void Fl_WinAPI_System_Driver::gettime(time_t *sec, int *usec) { - struct _timeb t; - _ftime(&t); - *sec = t.time; - *usec = t.millitm * 1000; -} - -// -// Code for lock support -// - -// These pointers are in Fl_win32.cxx: -extern void (*fl_lock_function)(); -extern void (*fl_unlock_function)(); - -// The main thread's ID -static DWORD main_thread; - -// Microsoft's version of a MUTEX... -static CRITICAL_SECTION cs; -static CRITICAL_SECTION *cs_ring; - -void Fl_WinAPI_System_Driver::unlock_ring() { - LeaveCriticalSection(cs_ring); -} - -void Fl_WinAPI_System_Driver::lock_ring() { - if (!cs_ring) { - cs_ring = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); - InitializeCriticalSection(cs_ring); - } - EnterCriticalSection(cs_ring); -} - -// -// 'unlock_function()' - Release the lock. -// - -static void unlock_function() { - LeaveCriticalSection(&cs); -} - -// -// 'lock_function()' - Get the lock. -// - -static void lock_function() { - EnterCriticalSection(&cs); -} - -int Fl_WinAPI_System_Driver::lock() { - if (!main_thread) InitializeCriticalSection(&cs); - - lock_function(); - - if (!main_thread) { - fl_lock_function = lock_function; - fl_unlock_function = unlock_function; - main_thread = GetCurrentThreadId(); - } - return 0; -} - -void Fl_WinAPI_System_Driver::unlock() { - unlock_function(); -} - -void Fl_WinAPI_System_Driver::awake(void* msg) { - PostThreadMessage( main_thread, fl_wake_msg, (WPARAM)msg, 0); -} - -int Fl_WinAPI_System_Driver::close_fd(int fd) { - return _close(fd); -} diff --git a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H deleted file mode 100644 index 3cf26b67c..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H +++ /dev/null @@ -1,131 +0,0 @@ -// -// Definition of Windows window driver -// for the Fast Light Tool Kit (FLTK). -// -// Copyright 2010-2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -/** - \file Fl_WinAPI_Window_Driver.H - \brief Definition of Windows window driver. - */ - -#ifndef FL_WINAPI_WINDOW_DRIVER_H -#define FL_WINAPI_WINDOW_DRIVER_H - -#include -#include "../../Fl_Window_Driver.H" -#include - -/* - Move everything here that manages the native window interface. - - There is one window driver for each Fl_Window. Window drivers manage window - actions such as resizing, events, decoration, fullscreen modes, etc. . All - drawing and rendering is managed by the Surface device and the associated - graphics driver. - - - window specific event handling - - window types and styles, depth, etc. - - decorations - - ? where do we handle the interface between OpenGL/DirectX and Cocoa/Windows/Glx? - */ - - - -class Fl_WinAPI_Window_Driver : public Fl_Window_Driver -{ - struct icon_data { - const void *legacy_icon; - Fl_RGB_Image **icons; - int count; - HICON big_icon; - HICON small_icon; - }; - struct shape_data_type { - int lw_; ///< width of shape image - int lh_; ///< height of shape image - Fl_Image* shape_; ///< shape image - Fl_Bitmap *effective_bitmap_; ///< auxiliary bitmap image - } *shape_data_; -private: - void shape_bitmap_(Fl_Image* b); - void shape_alpha_(Fl_Image* img, int offset) FL_OVERRIDE; -public: - Fl_WinAPI_Window_Driver(Fl_Window*); - ~Fl_WinAPI_Window_Driver(); - static inline Fl_WinAPI_Window_Driver* driver(const Fl_Window *w) {return (Fl_WinAPI_Window_Driver*)Fl_Window_Driver::driver(w);} - HDC private_dc; // used for OpenGL - RECT border_width_title_bar_height(int &bx, int &by, int &bt); - - struct icon_data *icon_; - HCURSOR cursor; - int custom_cursor; - void set_minmax(LPMINMAXINFO minmax); - int fake_X_wm(int &X, int &Y, int &bt, int &bx, int &by, DWORD style = 0, DWORD styleEx = 0); - void make_fullscreen(int X, int Y, int W, int H); - // --- window data - int decorated_w() FL_OVERRIDE; - int decorated_h() FL_OVERRIDE; - const Fl_Image* shape() FL_OVERRIDE; - - // --- window management - void makeWindow() FL_OVERRIDE; - void size_range() FL_OVERRIDE { - // currently nothing to do - } - void flush_double() FL_OVERRIDE; - void flush_overlay() FL_OVERRIDE; - void draw_begin() FL_OVERRIDE; - void make_current() FL_OVERRIDE; - void show() FL_OVERRIDE; - void label(const char *name,const char *iname) FL_OVERRIDE; - void resize(int X,int Y,int W,int H) FL_OVERRIDE; - void hide() FL_OVERRIDE; - void map() FL_OVERRIDE; - void unmap() FL_OVERRIDE; - void fullscreen_on() FL_OVERRIDE; - void fullscreen_off(int X, int Y, int W, int H) FL_OVERRIDE; - void maximize() FL_OVERRIDE; - void un_maximize() FL_OVERRIDE; - bool maximize_needs_hide() FL_OVERRIDE { return true; } - void iconize() FL_OVERRIDE; - void decoration_sizes(int *top, int *left, int *right, int *bottom) FL_OVERRIDE; - // --- window cursor stuff - int set_cursor(Fl_Cursor) FL_OVERRIDE; - int set_cursor(const Fl_RGB_Image*, int, int) FL_OVERRIDE; - - void shape(const Fl_Image* img) FL_OVERRIDE; - void icons(const Fl_RGB_Image *icons[], int count) FL_OVERRIDE; - const void *icon() const FL_OVERRIDE; - void icon(const void * ic) FL_OVERRIDE; - void free_icons() FL_OVERRIDE; - void set_icons(); // driver-internal support function - // this one is implemented in Fl_win32.cxx - void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) FL_OVERRIDE; - int scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, - void (*draw_area)(void*, int,int,int,int), void* data) FL_OVERRIDE; -}; - - -class Fl_WinAPI_Plugin : public Fl_Plugin { -public: - Fl_WinAPI_Plugin(const char *pluginName) : Fl_Plugin(klass(), pluginName) { } - virtual const char *klass() { return "winapi.fltk.org"; } - virtual const char *name() = 0; - virtual void invalidate(Fl_Window*) = 0; -}; - - -#endif // FL_WINAPI_WINDOW_DRIVER_H diff --git a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx deleted file mode 100644 index 4b27753bf..000000000 --- a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx +++ /dev/null @@ -1,731 +0,0 @@ -// -// Definition of Windows window driver for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2025 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Fl_WinAPI_Window_Driver.H" -#include "Fl_WinAPI_Screen_Driver.H" -#include "../GDI/Fl_GDI_Graphics_Driver.H" -#include -#include -#include // for ceil() - -#if USE_COLORMAP -extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx -#endif - - -Fl_WinAPI_Window_Driver::Fl_WinAPI_Window_Driver(Fl_Window *win) -: Fl_Window_Driver(win) -{ - icon_ = new icon_data; - shape_data_ = NULL; - memset(icon_, 0, sizeof(icon_data)); - cursor = NULL; - screen_num_ = -1; -} - - -Fl_WinAPI_Window_Driver::~Fl_WinAPI_Window_Driver() -{ - if (shape_data_) { - delete shape_data_->effective_bitmap_; - delete shape_data_; - } - delete icon_; -} - - -//FILE*LOG=fopen("log.log","w"); - - -RECT // frame of the decorated window in screen coordinates - Fl_WinAPI_Window_Driver::border_width_title_bar_height( - int &bx, // left and right border width - int &by, // bottom border height (=bx) - int &bt // height of window title bar - ) -{ - Fl_Window *win = pWindow; - RECT r = {0,0,0,0}; - bx = by = bt = 0; - if (win->shown() && !win->parent() && win->border() && win->visible()) { - static HMODULE dwmapi_dll = LoadLibrary("dwmapi.dll"); - typedef HRESULT (WINAPI* DwmGetWindowAttribute_type)(HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute); - static DwmGetWindowAttribute_type DwmGetWindowAttribute = dwmapi_dll ? - (DwmGetWindowAttribute_type)GetProcAddress(dwmapi_dll, "DwmGetWindowAttribute") : NULL; - int need_r = 1; - if (DwmGetWindowAttribute) { - const DWORD DWMWA_EXTENDED_FRAME_BOUNDS = 9; - if ( DwmGetWindowAttribute(fl_xid(win), DWMWA_EXTENDED_FRAME_BOUNDS, &r, sizeof(RECT)) == S_OK ) { - need_r = 0; - } - } - if (need_r) { - GetWindowRect(fl_xid(win), &r); - } - int width, height; - RECT rc; - GetClientRect(fl_xid(win), &rc); - width = rc.right; - height = rc.bottom; - bx = (r.right - r.left - width)/2; - if (bx < 1) bx = 1; - by = bx; - bt = r.bottom - r.top - height - 2 * by; - } - return r; -} - - -// --- window data - -int Fl_WinAPI_Window_Driver::decorated_w() -{ - int bt, bx, by; - float s = Fl::screen_driver()->scale(screen_num()); - border_width_title_bar_height(bx, by, bt); - int mini_bx = int(bx/s); if (mini_bx < 1) mini_bx = 1; - return w() + 2 * mini_bx; -} - -int Fl_WinAPI_Window_Driver::decorated_h() -{ - int bt, bx, by; - border_width_title_bar_height(bx, by, bt); - float s = Fl::screen_driver()->scale(screen_num()); - int mini_by = int(by / s); if (mini_by < 1) mini_by = 1; - return h() + int((bt + by) / s) + mini_by; -} - - -// --- window management - - - -void Fl_WinAPI_Window_Driver::shape_bitmap_(Fl_Image* b) { - shape_data_->shape_ = b; -} - -void Fl_WinAPI_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { - int i, j, d = img->d(), w = img->w(), h = img->h(), bytesperrow = (w+7)/8; - unsigned u; - uchar byte, onebit; - // build an Fl_Bitmap covering the non-fully transparent/black part of the image - const uchar* bits = new uchar[h*bytesperrow]; // to store the bitmap - const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of rgba pixels - for (i = 0; i < h; i++) { - uchar *p = (uchar*)bits + i * bytesperrow; - byte = 0; - onebit = 1; - for (j = 0; j < w; j++) { - if (d == 3) { - u = *alpha; - u += *(alpha+1); - u += *(alpha+2); - } - else u = *alpha; - if (u > 0) { // if the pixel is not fully transparent/black - byte |= onebit; // turn on the corresponding bit of the bitmap - } - onebit = onebit << 1; // move the single set bit one position to the left - if (onebit == 0 || j == w-1) { - onebit = 1; - *p++ = byte; // store in bitmap one pack of bits - byte = 0; - } - alpha += d; // point to alpha value of next pixel - } - } - Fl_Bitmap* bitmap = new Fl_Bitmap(bits, w, h); - bitmap->alloc_array = 1; - shape_bitmap_(bitmap); - shape_data_->effective_bitmap_ = bitmap; - shape_data_->shape_ = img; -} - -void Fl_WinAPI_Window_Driver::shape(const Fl_Image* img) { - if (shape_data_) { - if (shape_data_->effective_bitmap_) { delete shape_data_->effective_bitmap_; } - } - else { - shape_data_ = new shape_data_type; - } - memset(shape_data_, 0, sizeof(shape_data_type)); - pWindow->border(false); - int d = img->d(); - if (d && img->count() >= 2) { - shape_pixmap_((Fl_Image*)img); - shape_data_->shape_ = (Fl_Image*)img; - } - else if (d == 0) shape_bitmap_((Fl_Image*)img); - else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); - else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); -} - - -static inline BYTE bit(int x) { return (BYTE)(1 << (x%8)); } - -static HRGN bitmap2region(Fl_Image* image) { - HRGN hRgn = 0; - /* Does this need to be dynamically determined, perhaps? */ - const int ALLOC_UNIT = 100; - DWORD maxRects = ALLOC_UNIT; - - RGNDATA* pData = (RGNDATA*)malloc(sizeof(RGNDATAHEADER)+(sizeof(RECT)*maxRects)); - pData->rdh.dwSize = sizeof(RGNDATAHEADER); - pData->rdh.iType = RDH_RECTANGLES; - pData->rdh.nCount = pData->rdh.nRgnSize = 0; - SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); - - const int bytesPerLine = (image->w() + 7)/8; - BYTE* p, *data = (BYTE*)*image->data(); - for (int y = 0; y < image->h(); y++) { - // each row, left to right - for (int x = 0; x < image->w(); x++) { - int x0 = x; - while (x < image->w()) { - p = data + x / 8; - if (!((*p) & bit(x))) break; // transparent pixel - x++; - } - if (x > x0) { - RECT *pr; - /* Add the pixels (x0, y) to (x, y+1) as a new rectangle - * in the region - */ - if (pData->rdh.nCount >= maxRects) { - maxRects += ALLOC_UNIT; - pData = (RGNDATA*)realloc(pData, sizeof(RGNDATAHEADER) - + (sizeof(RECT)*maxRects)); - } - pr = (RECT*)&pData->Buffer; - SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1); - if (x0 < pData->rdh.rcBound.left) - pData->rdh.rcBound.left = x0; - if (y < pData->rdh.rcBound.top) - pData->rdh.rcBound.top = y; - if (x > pData->rdh.rcBound.right) - pData->rdh.rcBound.right = x; - if (y+1 > pData->rdh.rcBound.bottom) - pData->rdh.rcBound.bottom = y+1; - pData->rdh.nCount++; - /* On Windows98, ExtCreateRegion() may fail if the - * number of rectangles is too large (ie: > - * 4000). Therefore, we have to create the region by - * multiple steps. - */ - if (pData->rdh.nCount == 2000) { - HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) - + (sizeof(RECT)*maxRects), pData); - if (hRgn) { - CombineRgn(hRgn, hRgn, h, RGN_OR); - DeleteObject(h); - } else - hRgn = h; - pData->rdh.nCount = 0; - SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); - } - } - } - /* Go to next row */ - data += bytesPerLine; - } - /* Create or extend the region with the remaining rectangles*/ - HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) - + (sizeof(RECT)*maxRects), pData); - if (hRgn) { - CombineRgn(hRgn, hRgn, h, RGN_OR); - DeleteObject(h); - } else hRgn = h; - free(pData); // I've created the region so I can free this now, right? - return hRgn; -} - - -void Fl_WinAPI_Window_Driver::draw_begin() -{ - if (shape_data_) { - float s = Fl::screen_driver()->scale(screen_num()); - if ((shape_data_->lw_ != s*w() || shape_data_->lh_ != s*h()) && shape_data_->shape_) { - // size of window has changed since last time - shape_data_->lw_ = int(s * w()); - shape_data_->lh_ = int(s * h()); - Fl_Image* temp = shape_data_->effective_bitmap_ ? shape_data_->effective_bitmap_ : shape_data_->shape_; - temp = temp->copy(shape_data_->lw_, shape_data_->lh_); - HRGN region = bitmap2region(temp); - SetWindowRgn(fl_xid(pWindow), region, TRUE); // the system deletes the region when it's no longer needed - delete temp; - } - } -} - - -void Fl_WinAPI_Window_Driver::flush_double() -{ - if (!shown()) return; - pWindow->make_current(); // make sure fl_gc is non-zero - Fl_X *i = Fl_X::flx(pWindow); - if (!i) return; // window not yet created - - if (!other_xid) { - other_xid = new Fl_Image_Surface(w(), h(), 1); - pWindow->clear_damage(FL_DAMAGE_ALL); - } - if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) { - fl_clip_region(i->region); i->region = 0; -#if 0 /* Short form that transiently changes the current Fl_Surface_Device */ - Fl_Surface_Device::push_current(other_xid); - fl_graphics_driver->clip_region( 0 ); - draw(); - Fl_Surface_Device::pop_current(); -#else - /* Alternative form that avoids changing the current Fl_Surface_Device. - The code run in the window draw() method can call Fl_Surface_Device::surface() - and conclude that it's drawing to the display, which is ultimately true - for an Fl_Double_Window. - */ - HDC sgc = fl_gc; - fl_gc = fl_makeDC((HBITMAP)other_xid->offscreen()); - int savedc = SaveDC(fl_gc); - fl_graphics_driver->gc(fl_gc); - fl_graphics_driver->restore_clip(); // duplicate clip region into new gc -# if defined(FLTK_HAVE_CAIROEXT) - if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow); -# endif - draw(); - RestoreDC(fl_gc, savedc); - DeleteDC(fl_gc); - fl_graphics_driver->gc(sgc); -#endif - } - int X = 0, Y = 0, W = 0, H = 0; - fl_clip_box(0, 0, w(), h(), X, Y, W, H); - if (other_xid) fl_copy_offscreen(X, Y, W, H, other_xid->offscreen(), X, Y); -} - - -void Fl_WinAPI_Window_Driver::flush_overlay() -{ - Fl_Overlay_Window *oWindow = pWindow->as_overlay_window(); - - if (!shown()) return; - pWindow->make_current(); // make sure fl_gc is non-zero - Fl_X *i = Fl_X::flx(pWindow); - if (!i) return; // window not yet created - - int eraseoverlay = (pWindow->damage()&FL_DAMAGE_OVERLAY); - pWindow->clear_damage((uchar)(pWindow->damage()&~FL_DAMAGE_OVERLAY)); - - if (!other_xid) { - other_xid = new Fl_Image_Surface(w(), h(), 1); - pWindow->clear_damage(FL_DAMAGE_ALL); - } - if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) { - fl_clip_region(i->region); i->region = 0; - Fl_Surface_Device::push_current(other_xid); - fl_graphics_driver->clip_region(0); - draw(); - Fl_Surface_Device::pop_current(); - } - - if (eraseoverlay) fl_clip_region(0); - int X, Y, W, H; fl_clip_box(0, 0, w(), h(), X, Y, W, H); - if (other_xid) fl_copy_offscreen(X, Y, W, H, other_xid->offscreen(), X, Y); - - if (overlay() == oWindow) oWindow->draw_overlay(); -} - - -void Fl_WinAPI_Window_Driver::icons(const Fl_RGB_Image *icons[], int count) { - free_icons(); - - if (count > 0) { - icon_->icons = new Fl_RGB_Image*[count]; - icon_->count = count; - // FIXME: Fl_RGB_Image lacks const modifiers on methods - for (int i = 0;i < count;i++) { - icon_->icons[i] = (Fl_RGB_Image*)((Fl_RGB_Image*)icons[i])->copy(); - icon_->icons[i]->normalize(); - } - } - - if (Fl_X::flx(pWindow)) - set_icons(); -} - -const void *Fl_WinAPI_Window_Driver::icon() const { - return icon_->legacy_icon; -} - -void Fl_WinAPI_Window_Driver::icon(const void * ic) { - free_icons(); - icon_->legacy_icon = ic; -} - -void Fl_WinAPI_Window_Driver::free_icons() { - int i; - icon_->legacy_icon = 0L; - if (icon_->icons) { - for (i = 0;i < icon_->count;i++) - delete icon_->icons[i]; - delete [] icon_->icons; - icon_->icons = 0L; - } - icon_->count = 0; - if (icon_->big_icon) - DestroyIcon(icon_->big_icon); - if (icon_->small_icon) - DestroyIcon(icon_->small_icon); - icon_->big_icon = NULL; - icon_->small_icon = NULL; -} - - -void Fl_WinAPI_Window_Driver::make_current() { - fl_GetDC(fl_xid(pWindow)); - -#if USE_COLORMAP - // Windows maintains a hardware and software color palette; the - // SelectPalette() call updates the current soft->hard mapping - // for all drawing calls, so we must select it here before any - // code does any drawing... - fl_select_palette(); -#endif // USE_COLORMAP - - fl_graphics_driver->clip_region(0); - ((Fl_GDI_Graphics_Driver*)fl_graphics_driver)->scale(Fl::screen_driver()->scale(screen_num())); -#if defined(FLTK_HAVE_CAIROEXT) - if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow); -#endif -} - -void Fl_WinAPI_Window_Driver::label(const char *name,const char *iname) { - if (shown() && !parent()) { - if (!name) name = ""; - size_t l = strlen(name); - // WCHAR *lab = (WCHAR*) malloc((l + 1) * sizeof(short)); - // l = fl_utf2unicode((unsigned char*)name, l, (wchar_t*)lab); - unsigned wlen = fl_utf8toUtf16(name, (unsigned) l, NULL, 0); // Pass NULL to query length - wlen++; - unsigned short * lab = (unsigned short*)malloc(sizeof(unsigned short)*wlen); - wlen = fl_utf8toUtf16(name, (unsigned) l, lab, wlen); - lab[wlen] = 0; - SetWindowTextW(fl_xid(pWindow), (WCHAR *)lab); - free(lab); - } -} - - -extern void fl_clipboard_notify_retarget(HWND wnd); -extern void fl_update_clipboard(void); -extern char fl_i_own_selection[2]; - -void Fl_WinAPI_Window_Driver::hide() { - Fl_X* ip = Fl_X::flx(pWindow); - // STR#3079: if there remains a window and a non-modal window, and the window is deleted, - // the app remains running without any apparent window. - // Bug mechanism: hiding an owner window unmaps the owned (non-modal) window(s) - // but does not delete it(them) in FLTK. - // Fix for it: - // when hiding a window, build list of windows it owns, and do hide/show on them. - int count = 0; - Fl_Window *win, **doit = NULL; - for (win = Fl::first_window(); win && ip; win = Fl::next_window(win)) { - if (win->non_modal() && GetWindow(fl_xid(win), GW_OWNER) == (HWND)ip->xid) { - count++; - } - } - if (count) { - doit = new Fl_Window*[count]; - count = 0; - for (win = Fl::first_window(); win && ip; win = Fl::next_window(win)) { - if (win->non_modal() && GetWindow(fl_xid(win), GW_OWNER) == (HWND)ip->xid) { - doit[count++] = win; - } - } - } - - if (hide_common()) { - delete[] doit; // note: `count` and `doit` may be NULL (see PR #241) - return; - } - - // Issue #569: undo RegisterDragDrop() - RevokeDragDrop((HWND)ip->xid); - - fl_i_own_selection[1] = 0; // issue #1233 - - // make sure any custom icons get freed - // icons(NULL, 0); // free_icons() is called by the Fl_Window destructor - // this little trick keeps the current clipboard alive, even if we are about - // to destroy the window that owns the selection. - if (GetClipboardOwner() == (HWND)ip->xid) - fl_update_clipboard(); - // Make sure we unlink this window from the clipboard chain - fl_clipboard_notify_retarget((HWND)ip->xid); - // Send a message to myself so that I'll get out of the event loop... - PostMessage((HWND)ip->xid, WM_APP, 0, 0); - if (private_dc) fl_release_dc((HWND)ip->xid, private_dc); - if ((HWND)ip->xid == fl_window && fl_graphics_driver->gc()) { - fl_release_dc(fl_window, (HDC)fl_graphics_driver->gc()); - fl_window = (HWND)-1; - fl_graphics_driver->gc(0); -# ifdef FLTK_HAVE_CAIROEXT - if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); -# endif - } - - if (ip->region) Fl_Graphics_Driver::default_driver().XDestroyRegion(ip->region); - - // this little trickery seems to avoid the popup window stacking problem - HWND p = GetForegroundWindow(); - if (p==GetParent((HWND)ip->xid)) { - ShowWindow((HWND)ip->xid, SW_HIDE); - ShowWindow(p, SW_SHOWNA); - } - DestroyWindow((HWND)ip->xid); - // end of fix for STR#3079 - if (count) { - int ii; - for (ii = 0; ii < count; ii++) doit[ii]->hide(); - for (ii = 0; ii < count; ii++) { - if (ii != 0) doit[0]->show(); // Fix for STR#3165 - doit[ii]->show(); - } - } - delete[] doit; // note: `count` and `doit` may be NULL (see PR #241) - - // Try to stop the annoying "raise another program" behavior - if (pWindow->non_modal() && Fl::first_window() && Fl::first_window()->shown()) - Fl::first_window()->show(); - delete ip; - screen_num_ = -1; -} - - -void Fl_WinAPI_Window_Driver::map() { - ShowWindow(fl_xid(pWindow), SW_RESTORE); // extra map calls are harmless -} - - -void Fl_WinAPI_Window_Driver::unmap() { - ShowWindow(fl_xid(pWindow), SW_HIDE); -} - -#if !defined(FL_DOXYGEN) // FIXME - silence Doxygen warning - -void Fl_WinAPI_Window_Driver::make_fullscreen(int X, int Y, int W, int H) { - HWND xid = fl_xid(pWindow); - int top, bottom, left, right; - int sx, sy, sw, sh; - - top = fullscreen_screen_top(); - bottom = fullscreen_screen_bottom(); - left = fullscreen_screen_left(); - right = fullscreen_screen_right(); - - if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) { - top = screen_num(); - bottom = top; - left = top; - right = top; - } - - Fl_WinAPI_Screen_Driver *scr_dr = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver(); - scr_dr->screen_xywh_unscaled(sx, Y, sw, sh, top); - scr_dr->screen_xywh_unscaled(sx, sy, sw, sh, bottom); - H = sy + sh - Y; - scr_dr->screen_xywh_unscaled(X, sy, sw, sh, left); - scr_dr->screen_xywh_unscaled(sx, sy, sw, sh, right); - W = sx + sw - X; - - DWORD flags = GetWindowLong(xid, GWL_STYLE); - flags = flags & ~(WS_THICKFRAME|WS_CAPTION); - SetWindowLong(xid, GWL_STYLE, flags); - - // SWP_NOSENDCHANGING is so that we can override size limits - SetWindowPos(xid, HWND_TOP, X, Y, W, H, SWP_NOSENDCHANGING | SWP_FRAMECHANGED); -} - -#endif // !defined(FL_DOXYGEN) // FIXME - silence Doxygen warning - - -void Fl_WinAPI_Window_Driver::fullscreen_on() { - pWindow->_set_fullscreen(); - make_fullscreen(x(), y(), w(), h()); - Fl::handle(FL_FULLSCREEN, pWindow); -} - - -void Fl_WinAPI_Window_Driver::fullscreen_off(int X, int Y, int W, int H) { - pWindow->_clear_fullscreen(); - DWORD style = GetWindowLong(fl_xid(pWindow), GWL_STYLE); - if (pWindow->border()) style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION; - // Remove the xid temporarily so that Fl_WinAPI_Window_Driver::fake_X_wm() behaves like it - // does in Fl_WinAPI_Window_Driver::makeWindow(). - HWND xid = fl_xid(pWindow); - Fl_X::flx(pWindow)->xid = 0; - int wx, wy, bt, bx, by; - switch (fake_X_wm(wx, wy, bt, bx, by, style)) { - case 0: - break; - case 1: - style |= WS_CAPTION; - break; - case 2: - /*if (border()) { - style |= WS_THICKFRAME | WS_CAPTION; - }*/ - break; - } - Fl_X::flx(pWindow)->xid = (fl_uintptr_t)xid; - SetWindowLong(fl_xid(pWindow), GWL_STYLE, style); - if (!pWindow->maximize_active()) { - // compute window position and size in scaled units - float s = Fl::screen_driver()->scale(screen_num()); - int scaledX = int(ceil(X*s)), scaledY= int(ceil(Y*s)), scaledW = int(ceil(W*s)), scaledH = int(ceil(H*s)); - // Adjust for decorations (but not if that puts the decorations - // outside the screen) - if ((X != x()) || (Y != y())) { - scaledX -= bx; - scaledY -= by+bt; - } - scaledW += bx*2; - scaledH += by*2+bt; - SetWindowPos(fl_xid(pWindow), 0, scaledX, scaledY, scaledW, scaledH, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); - } else { - int WX, WY, WW, WH; - ((Fl_WinAPI_Screen_Driver*)Fl::screen_driver())->screen_xywh_unscaled(WX, WY, WW, WH, screen_num()); - SetWindowPos(fl_xid(pWindow), 0, WX, WY, WW, WH, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); - } - Fl::handle(FL_FULLSCREEN, pWindow); -} - - -void Fl_WinAPI_Window_Driver::maximize() { - if (!border()) return Fl_Window_Driver::maximize(); - ShowWindow(fl_xid(pWindow), SW_SHOWMAXIMIZED); -} - -void Fl_WinAPI_Window_Driver::un_maximize() { - if (!border()) return Fl_Window_Driver::un_maximize(); - ShowWindow(fl_xid(pWindow), SW_SHOWNORMAL); -} - - -void Fl_WinAPI_Window_Driver::iconize() { - ShowWindow(fl_xid(pWindow), SW_SHOWMINNOACTIVE); -} - - -void Fl_WinAPI_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom) { - int minw, minh, maxw, maxh, set; - set = pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (set && (maxw != minw || maxh != minh)) { - *left = *right = GetSystemMetrics(SM_CXSIZEFRAME); - *top = *bottom = GetSystemMetrics(SM_CYSIZEFRAME); - } else { - *left = *right = GetSystemMetrics(SM_CXFIXEDFRAME); - *top = *bottom = GetSystemMetrics(SM_CYFIXEDFRAME); - } - *top += GetSystemMetrics(SM_CYCAPTION); -} - -int Fl_WinAPI_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, - void (*draw_area)(void*, int,int,int,int), void* data) -{ - typedef int (WINAPI* fl_GetRandomRgn_func)(HDC, HRGN, INT); - static fl_GetRandomRgn_func fl_GetRandomRgn = 0L; - static char first_time = 1; - // We will have to do some Region magic now, so let's see if the - // required function is available (and it should be starting w/Win95) - if (first_time) { - HMODULE hMod = GetModuleHandle("GDI32.DLL"); - if (hMod) { - fl_GetRandomRgn = (fl_GetRandomRgn_func)GetProcAddress(hMod, "GetRandomRgn"); - } - first_time = 0; - } - float s = Fl::screen_driver()->scale(screen_num()); - src_x = int(src_x * s); src_y = int(src_y * s); - src_w = int(src_w * s); src_h = int(src_h * s); - dest_x = int(dest_x * s); dest_y = int(dest_y * s); - // Now check if the source scrolling area is fully visible. - // If it is, we will do a quick scroll and just update the - // newly exposed area. If it is not, we go the safe route and - // re-render the full area instead. - // Note 1: we could go and find the areas that are actually - // obscured and recursively call fl_scroll for the newly found - // rectangles. However, this practice would rely on the - // elements of the undocumented Rgn structure. - // Note 2: although this method should take care of most - // multi-screen solutions, it will not solve issues scrolling - // from a different resolution screen onto another. - // Note 3: this has been tested with image maps, too. - HDC gc = (HDC)fl_graphics_driver->gc(); - if (fl_GetRandomRgn) { - // get the DC region minus all overlapping windows - HRGN sys_rgn = CreateRectRgn(0, 0, 0, 0); - fl_GetRandomRgn(gc, sys_rgn, 4); - // now get the source scrolling rectangle - HRGN src_rgn = CreateRectRgn(src_x, src_y, src_x+src_w, src_y+src_h); - POINT offset = { 0, 0 }; - if (GetDCOrgEx(gc, &offset)) { - OffsetRgn(src_rgn, offset.x, offset.y); - } - // see if all source pixels are available in the system region - // Note: we could be a bit more merciful and subtract the - // scroll destination region as well. - HRGN dst_rgn = CreateRectRgn(0, 0, 0, 0); - int r = CombineRgn(dst_rgn, src_rgn, sys_rgn, RGN_DIFF); - DeleteObject(dst_rgn); - DeleteObject(src_rgn); - DeleteObject(sys_rgn); - if (r != NULLREGION) { - return 1; - } - } - // Great, we can do an accelerated scroll instead of re-rendering - BitBlt(gc, dest_x, dest_y, src_w, src_h, gc, src_x, src_y,SRCCOPY); - return 0; -} - - -const Fl_Image* Fl_WinAPI_Window_Driver::shape() { - return shape_data_ ? shape_data_->shape_ : NULL; -} - - -HWND fl_win32_xid(const Fl_Window *win) { - return (HWND)Fl_Window_Driver::xid(win); -} - - -Fl_Window *fl_win32_find(HWND xid) { - return Fl_Window_Driver::find((fl_uintptr_t)xid); -} diff --git a/src/drivers/WinAPI/fl_WinAPI_platform_init.cxx b/src/drivers/WinAPI/fl_WinAPI_platform_init.cxx deleted file mode 100644 index b5ffe2449..000000000 --- a/src/drivers/WinAPI/fl_WinAPI_platform_init.cxx +++ /dev/null @@ -1,85 +0,0 @@ -// -// Windows-specific code to initialize Windows support. -// -// Copyright 2022 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - - -#include "../GDI/Fl_GDI_Copy_Surface_Driver.H" -#include "../GDI/Fl_GDI_Graphics_Driver.H" -#include "Fl_WinAPI_Screen_Driver.H" -#include "Fl_WinAPI_System_Driver.H" -#include "Fl_WinAPI_Window_Driver.H" -#include "../GDI/Fl_GDI_Image_Surface_Driver.H" -#include "../Base/Fl_Base_Pen_Events.H" - - -Fl_Copy_Surface_Driver *Fl_Copy_Surface_Driver::newCopySurfaceDriver(int w, int h) -{ - return new Fl_GDI_Copy_Surface_Driver(w, h); -} - - -Fl_Graphics_Driver *Fl_Graphics_Driver::newMainGraphicsDriver() -{ -#if USE_GDIPLUS - // Initialize GDI+. - static Gdiplus::GdiplusStartupInput gdiplusStartupInput; - if (Fl_GDIplus_Graphics_Driver::gdiplus_state_ == Fl_GDIplus_Graphics_Driver::STATE_CLOSED) { - Fl_GDIplus_Graphics_Driver::gdiplus_state_ = Fl_GDIplus_Graphics_Driver::STATE_STARTUP; - Gdiplus::Status ret = GdiplusStartup(&Fl_GDIplus_Graphics_Driver::gdiplus_token_, &gdiplusStartupInput, NULL); - if (ret == 0) { // 0 is same as Gdiplus::Status::Ok, but old mingw64 barks at that - Fl_GDIplus_Graphics_Driver::gdiplus_state_ = Fl_GDIplus_Graphics_Driver::STATE_OPEN; - } else { - Fl::warning("GdiplusStartup failed with error code %d.", ret); - Fl_GDIplus_Graphics_Driver::gdiplus_state_ = Fl_GDIplus_Graphics_Driver::STATE_CLOSED; - return new Fl_GDI_Graphics_Driver(); - } - } else if (Fl_GDIplus_Graphics_Driver::gdiplus_state_ == Fl_GDIplus_Graphics_Driver::STATE_OPEN) { -// Fl::warning("GdiplusStartup() called, but driver is already open."); - } else if (Fl_GDIplus_Graphics_Driver::gdiplus_state_ == Fl_GDIplus_Graphics_Driver::STATE_SHUTDOWN) { -// Fl::warning("GdiplusStartup() called while driver is shutting down."); - } else if (Fl_GDIplus_Graphics_Driver::gdiplus_state_ == Fl_GDIplus_Graphics_Driver::STATE_STARTUP) { -// Fl::warning("GdiplusStartup() called recursively."); - } - Fl_Graphics_Driver *driver = new Fl_GDIplus_Graphics_Driver(); - return driver; -#else - return new Fl_GDI_Graphics_Driver(); -#endif -} - - -Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver() -{ - return new Fl_WinAPI_Screen_Driver(); -} - - -Fl_System_Driver *Fl_System_Driver::newSystemDriver() -{ - return new Fl_WinAPI_System_Driver(); -} - - -Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w) -{ - return new Fl_WinAPI_Window_Driver(w); -} - - -Fl_Image_Surface_Driver *Fl_Image_Surface_Driver::newImageSurfaceDriver(int w, int h, int high_res, Fl_Offscreen off) -{ - return new Fl_GDI_Image_Surface_Driver(w, h, high_res, off); -} - diff --git a/src/drivers/X11/Fl_X11_Gl_Window_Driver.cxx b/src/drivers/X11/Fl_X11_Gl_Window_Driver.cxx index 7282528ca..822acc826 100644 --- a/src/drivers/X11/Fl_X11_Gl_Window_Driver.cxx +++ b/src/drivers/X11/Fl_X11_Gl_Window_Driver.cxx @@ -40,12 +40,10 @@ public: } }; -#ifndef FLTK_USE_WAYLAND Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w) { return new Fl_X11_Gl_Window_Driver(w); } -#endif void Fl_X11_Gl_Window_Driver::draw_string_legacy(const char* str, int n) { draw_string_legacy_get_list(str, n); diff --git a/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H b/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H index fa3c0ee90..3904fa19e 100644 --- a/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H +++ b/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.H @@ -20,7 +20,7 @@ #include #include #if FLTK_USE_CAIRO -# include +# include #endif // FLTK_USE_CAIRO class Fl_Image_Surface; diff --git a/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx b/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx index f60f748f5..281a6590e 100644 --- a/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx +++ b/src/drivers/Xlib/Fl_Xlib_Copy_Surface_Driver.cxx @@ -24,7 +24,7 @@ #if FLTK_USE_CAIRO # include # include "../Cairo/Fl_X11_Cairo_Graphics_Driver.H" -# include +# include #else # include "Fl_Xlib_Graphics_Driver.H" #endif // FLTK_USE_CAIRO diff --git a/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H b/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H index 382daf886..f90f2cc79 100644 --- a/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H +++ b/src/drivers/Xlib/Fl_Xlib_Image_Surface_Driver.H @@ -19,7 +19,7 @@ #include #if FLTK_USE_CAIRO -# include +# include #endif // FLTK_USE_CAIRO class Fl_Xlib_Image_Surface_Driver : public Fl_Image_Surface_Driver { diff --git a/src/fl_call_main.c b/src/fl_call_main.c deleted file mode 100644 index 31cd86c31..000000000 --- a/src/fl_call_main.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 1998-2023 by Bill Spitzak and others. - * - * fl_call_main() calls main() for you Windows people. Needs to be done in C - * because Borland C++ won't let you call main() from C++. - * - * This library is free software. Distribution and use rights are outlined in - * the file "COPYING" which should have been included with this file. If this - * file is missing or damaged, see the license at: - * - * https://www.fltk.org/COPYING.php - * - * Please see the following page on how to report bugs and issues: - * - * https://www.fltk.org/bugs.php - */ - -/* - * This WinMain() function can be overridden by an application and - * is provided for compatibility with programs written for other - * operating systems that conform to the ANSI standard entry point - * "main()". This will allow you to build a Windows Application - * without any special settings. - * - * You cannot have this WinMain() function in a DLL because it would have - * to call \c main() outside the DLL. Thus, this nifty feature is only - * available if you link to the static library. - * - * However, it is possible to build this module separately so you can - * use it in progams that link to the shared library. - * - * Currently the debug version of this library will create a console window - * for your application so you can put printf() statements for debugging or - * informational purposes. Ultimately we want to update this to always use - * the parent's console, but at present we have not identified a function - * or API in Microsoft(r) Windows(r) that allows for it. - */ - -/* - * Notes for FLTK developers: - * - * 1) Since FLTK 1.4.0 this file is compiled only on Windows, hence we don't - * need to test the _WIN32 macro. - * 2) This file must not call any FLTK library functions because this would - * not work with /both/ the DLL /and/ the static library (linkage stuff). - * 3) Converting the commandline arguments to UTF-8 is therefore implemented - * here *and* in the library but this seems to be an acceptable compromise. - * 4) (Unless someone finds a better solution, of course. Albrecht) - * 5) The condition "!defined(FL_DLL)" prevents building this in the shared - * library, i.e. "WinMain()" will not be defined in the shared lib (DLL). - * 6) The condition "!defined (__GNUC__)" prevents compilation of this - * module with MinGW, MSYS, and Cygwin which don't use WinMain(). - * 7) It is unclear if there are other build systems on Windows that need a - * WinMain() entry point. Earlier comments and code seem to indicate that - * Borland C++ would require it. -*/ - -#if !defined(FL_DLL) && !defined (__GNUC__) - -#include -#include -#include -#include - -extern int main(int, char *[]); - -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPSTR lpCmdLine, int nCmdShow) { - int rc; - int i; - int argc = 0; - char** argv = NULL; - - /* - * If we are compiling in debug mode, open a console window so - * we can see any printf's, etc... - * - * While we can detect if the program was run from the command-line - - * look at the CMDLINE environment variable, it will be "WIN" for - * programs started from the GUI - the shell seems to run all Windows - * applications in the background anyways... - */ - -#ifdef _DEBUG - AllocConsole(); - freopen("conin$", "r", stdin); - freopen("conout$", "w", stdout); - freopen("conout$", "w", stderr); -#endif /* _DEBUG */ - - /* Get the command line arguments as Windows Wide Character strings */ - LPWSTR *wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc); - - /* Allocate an array of 'argc + 1' string pointers */ - argv = (char **)malloc((argc + 1) * sizeof(char *)); - - /* Convert the command line arguments to UTF-8 */ - for (i = 0; i < argc; i++) { - /* find the required size of the buffer */ - int u8size = WideCharToMultiByte(CP_UTF8, /* CodePage */ - 0, /* dwFlags */ - wideArgv[i], /* lpWideCharStr */ - -1, /* cchWideChar */ - NULL, /* lpMultiByteStr */ - 0, /* cbMultiByte */ - NULL, /* lpDefaultChar */ - NULL); /* lpUsedDefaultChar */ - if (u8size > 0) { - char *strbuf = (char *)malloc(u8size); - int ret = WideCharToMultiByte(CP_UTF8, /* CodePage */ - 0, /* dwFlags */ - wideArgv[i], /* lpWideCharStr */ - -1, /* cchWideChar */ - strbuf, /* lpMultiByteStr */ - u8size, /* cbMultiByte */ - NULL, /* lpDefaultChar */ - NULL); /* lpUsedDefaultChar */ - if (ret) { - argv[i] = strbuf; - } else { - argv[i] = _strdup(""); - free(strbuf); - } - } else { - argv[i] = _strdup(""); - } - } - argv[argc] = NULL; /* required by C standard at end of list */ - - /* Free the wide character string array */ - LocalFree(wideArgv); - - /* Call the program's entry point main() */ - rc = main(argc, argv); - - /* Cleanup allocated memory for argv */ - for (int i = 0; i < argc; ++i) { - free((void *)argv[i]); - } - free((void *)argv); - - /* Close the console in debug mode */ - -#ifdef _DEBUG - fclose(stdin); - fclose(stdout); - fclose(stderr); -#endif /* _DEBUG */ - - return rc; -} - -#else - -/* STR# 2973: solves "empty translation unit" error */ -typedef int dummy; - -#endif /* !defined(FL_DLL) && !defined (__GNUC__) */ diff --git a/src/fl_dnd_win32.cxx b/src/fl_dnd_win32.cxx deleted file mode 100644 index abaa588cc..000000000 --- a/src/fl_dnd_win32.cxx +++ /dev/null @@ -1,559 +0,0 @@ -// -// Drag & Drop code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2023 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Note: this file contains platform specific code and will therefore -// not be processed by doxygen (see Doxyfile.in). - -// This file contains Windows-specific code for FLTK which is always linked -// in. Search other files for "_WIN32" or filenames ending in _win32.cxx -// for other system-specific code. - -#include -#include -#include -#include "Fl_Window_Driver.H" -#include -#include "drivers/WinAPI/Fl_WinAPI_Screen_Driver.H" -#include "flstring.h" -#include -#include -#include -#include -#include -#if defined(__CYGWIN__) -#include -#include -#endif - -extern char *fl_selection_buffer[2]; -extern int fl_selection_length[2]; -extern int fl_selection_buffer_length[2]; -extern char fl_i_own_selection[2]; -extern char *fl_locale2utf8(const char *s, UINT codepage = 0); -extern unsigned int fl_codepage; - -Fl_Window *fl_dnd_target_window = 0; - -#include -#include -#include - - -/** - subclass the IDropTarget to receive data from DnD operations - */ -class FLDropTarget : public IDropTarget -{ - DWORD m_cRefCount; // for "statistics" only (issue #569) - DWORD lastEffect; - int px, py; -public: - FLDropTarget() : m_cRefCount(0) { } // initialize - virtual ~FLDropTarget() { } - HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, LPVOID *ppvObject ) FL_OVERRIDE { - if (IID_IUnknown==riid || IID_IDropTarget==riid) - { - *ppvObject=this; - ((LPUNKNOWN)*ppvObject)->AddRef(); - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - ULONG STDMETHODCALLTYPE AddRef() FL_OVERRIDE { return ++m_cRefCount; } - ULONG STDMETHODCALLTYPE Release() FL_OVERRIDE { - long nTemp; - nTemp = --m_cRefCount; - // this is a static object, do not 'delete this' (issue #569) - return nTemp; - } - HRESULT STDMETHODCALLTYPE DragEnter( IDataObject *pDataObj, DWORD /*grfKeyState*/, POINTL pt, DWORD *pdwEffect) FL_OVERRIDE { - if( !pDataObj ) return E_INVALIDARG; - // set e_modifiers here from grfKeyState, set e_x and e_root_x - // check if FLTK handles this drag and return if it can't (i.e. BMP drag without filename) - POINT ppt; - Fl::e_x_root = ppt.x = pt.x; - Fl::e_y_root = ppt.y = pt.y; - HWND hWnd = WindowFromPoint( ppt ); - Fl_Window *target = fl_find( hWnd ); - if (target) { - float s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(target)->screen_num()); - Fl::e_x_root = int(Fl::e_x_root / s); - Fl::e_y_root = int(Fl::e_y_root / s); - Fl::e_x = Fl::e_x_root-target->x(); - Fl::e_y = Fl::e_y_root-target->y(); - } - fl_dnd_target_window = target; - px = pt.x; py = pt.y; - if (fillCurrentDragData(pDataObj)) { - // FLTK has no mechanism yet for the different drop effects, so we allow move and copy - if ( target && Fl::handle( FL_DND_ENTER, target ) ) - *pdwEffect = DROPEFFECT_MOVE|DROPEFFECT_COPY; //|DROPEFFECT_LINK; - else - *pdwEffect = DROPEFFECT_NONE; - } else { - *pdwEffect = DROPEFFECT_NONE; - } - lastEffect = *pdwEffect; - return S_OK; - } - HRESULT STDMETHODCALLTYPE DragOver( DWORD /*grfKeyState*/, POINTL pt, DWORD *pdwEffect) FL_OVERRIDE { - if ( px==pt.x && py==pt.y ) - { - *pdwEffect = lastEffect; - return S_OK; - } - if ( !fl_dnd_target_window ) - { - *pdwEffect = lastEffect = DROPEFFECT_NONE; - return S_OK; - } - // set e_modifiers here from grfKeyState, set e_x and e_root_x - Fl::e_x_root = pt.x; - Fl::e_y_root = pt.y; - if (fl_dnd_target_window) { - float s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(fl_dnd_target_window)->screen_num()); - Fl::e_x_root = int(Fl::e_x_root /s); - Fl::e_y_root = int(Fl::e_y_root /s); - Fl::e_x = Fl::e_x_root-fl_dnd_target_window->x(); - Fl::e_y = Fl::e_y_root-fl_dnd_target_window->y(); - } - if (fillCurrentDragData(0)) { - // Fl_Group will change DND_DRAG into DND_ENTER and DND_LEAVE if needed - if ( Fl::handle( FL_DND_DRAG, fl_dnd_target_window ) ) - *pdwEffect = DROPEFFECT_MOVE|DROPEFFECT_COPY; //|DROPEFFECT_LINK; - else - *pdwEffect = DROPEFFECT_NONE; - } else { - *pdwEffect = DROPEFFECT_NONE; - } - px = pt.x; py = pt.y; - lastEffect = *pdwEffect; - // show insert position if dnd'ing in the same window/process (STR #3209) - Fl::flush(); - return S_OK; - } - HRESULT STDMETHODCALLTYPE DragLeave() FL_OVERRIDE { - if ( fl_dnd_target_window && fillCurrentDragData(0)) - { - Fl::handle( FL_DND_LEAVE, fl_dnd_target_window ); - fl_dnd_target_window = 0; - clearCurrentDragData(); - } - return S_OK; - } - HRESULT STDMETHODCALLTYPE Drop( IDataObject *data, DWORD /*grfKeyState*/, POINTL pt, DWORD* /*pdwEffect*/) FL_OVERRIDE { - if ( !fl_dnd_target_window ) - return S_OK; - Fl_Window *target = fl_dnd_target_window; - fl_dnd_target_window = 0; - Fl::e_x_root = pt.x; - Fl::e_y_root = pt.y; - float s = Fl::screen_driver()->scale(Fl_Window_Driver::driver(target)->screen_num()); - Fl::e_x_root = int(Fl::e_x_root / s); - Fl::e_y_root = int(Fl::e_y_root / s); - if (target) { - Fl::e_x = Fl::e_x_root-target->x(); - Fl::e_y = Fl::e_y_root-target->y(); - } - // tell FLTK that the user released an object on this widget - if ( !Fl::handle( FL_DND_RELEASE, target ) ) - return S_OK; - - Fl_Widget *w = target; - while (w->parent()) w = w->window(); - HWND hwnd = fl_xid( (Fl_Window*)w ); - if (fillCurrentDragData(data)) { - int old_event = Fl::e_number; - char *a, *b; - a = b = currDragData; - while (*a) { // strip the CRLF pairs - if (*a == '\r' && a[1] == '\n') a++; - else *b++ = *a++; - } - *b = 0; - Fl::e_text = currDragData; - Fl::e_length = (int) (b - currDragData); - Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); // e_text will be invalid after this call - Fl::e_number = old_event; - SetForegroundWindow( hwnd ); - clearCurrentDragData(); - return S_OK; - } - return S_OK; - } -private: - - static IDataObject *currDragRef; - static char *currDragData; - static int currDragSize; - static char currDragResult; - - static void clearCurrentDragData() { - currDragRef = 0; - if (currDragData) free(currDragData); - currDragData = 0; - currDragSize = 0; - currDragResult = 0; - } - static char fillCurrentDragData(IDataObject *data) { - // shortcut through this whole procedure if there is no fresh data - if (!data) - return currDragResult; - // shortcut through this whole procedure if this is still the same drag event - // (* this is safe, because 'currDragRef' is cleared on Leave and Drop events) - if (data==currDragRef) - return currDragResult; - - // clear currDrag* for a new drag event - clearCurrentDragData(); - - currDragRef = data; - // fill currDrag* with UTF-8 data, if available - FORMATETC fmt = { 0 }; - STGMEDIUM medium = { 0 }; - fmt.tymed = TYMED_HGLOBAL; - fmt.dwAspect = DVASPECT_CONTENT; - fmt.lindex = -1; - fmt.cfFormat = CF_UNICODETEXT; - // if it is UNICODE text, return a UTF-8-converted copy of it - if ( data->GetData( &fmt, &medium )==S_OK ) - { - void *stuff = GlobalLock( medium.hGlobal ); - unsigned srclen = 0; - const wchar_t *wstuff = (const wchar_t *)stuff; - while (*wstuff++) srclen++; - wstuff = (const wchar_t *)stuff; - unsigned utf8len = fl_utf8fromwc(NULL, 0, wstuff, srclen); - currDragSize = utf8len; - currDragData = (char*)malloc(utf8len + 1); - fl_utf8fromwc(currDragData, currDragSize+1, wstuff, srclen+1); // include null-byte - GlobalUnlock( medium.hGlobal ); - ReleaseStgMedium( &medium ); - currDragResult = 1; - return currDragResult; - } - fmt.cfFormat = CF_TEXT; - // if it is CP1252 text, return a UTF-8-converted copy of it - if ( data->GetData( &fmt, &medium )==S_OK ) - { - int len; - char *p, *q, *last; - unsigned u; - void *stuff = GlobalLock( medium.hGlobal ); - currDragData = (char*)malloc(3 * strlen((char*)stuff) + 10); - p = (char*)stuff; - last = p + strlen(p); - q = currDragData; - while (p < last) { - u = fl_utf8decode(p, last, &len); - p += len; - len = fl_utf8encode(u, q); - q += len; - } - *q = 0; - currDragSize = (int) (q - currDragData); - currDragData = (char*)realloc(currDragData, currDragSize + 1); - GlobalUnlock( medium.hGlobal ); - ReleaseStgMedium( &medium ); - currDragResult = 1; - return currDragResult; - } - // else fill currDrag* with filenames, if possible - memset(&fmt, 0, sizeof(fmt)); - fmt.tymed = TYMED_HGLOBAL; - fmt.dwAspect = DVASPECT_CONTENT; - fmt.lindex = -1; - fmt.cfFormat = CF_HDROP; - // if it is a pathname list, send an FL_PASTE with a \n separated list of filepaths - if ( data->GetData( &fmt, &medium )==S_OK ) - { - HDROP hdrop = (HDROP)medium.hGlobal; - int i, n, nn = 0, nf = DragQueryFileW( hdrop, (UINT)-1, 0, 0 ); - for ( i=0; ihandle(FL_DROP); -// free( Fl::e_text ); - ReleaseStgMedium( &medium ); - currDragResult = 1; - return currDragResult; - } - currDragResult = 0; - return currDragResult; - } -} flDropTarget; - -IDropTarget *flIDropTarget = &flDropTarget; - -IDataObject *FLDropTarget::currDragRef = 0; -char *FLDropTarget::currDragData = 0; -int FLDropTarget::currDragSize = 0; -char FLDropTarget::currDragResult = 0; - -/** - this class is needed to allow FLTK apps to be a DnD source - */ -class FLDropSource : public IDropSource -{ - DWORD m_cRefCount; -public: - FLDropSource() { m_cRefCount = 0; } - virtual ~FLDropSource() { } - HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, LPVOID *ppvObject ) FL_OVERRIDE { - if (IID_IUnknown==riid || IID_IDropSource==riid) - { - *ppvObject=this; - ((LPUNKNOWN)*ppvObject)->AddRef(); - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - ULONG STDMETHODCALLTYPE AddRef() FL_OVERRIDE { return ++m_cRefCount; } - ULONG STDMETHODCALLTYPE Release() FL_OVERRIDE { - long nTemp; - nTemp = --m_cRefCount; - if(nTemp==0) - delete this; - return nTemp; - } - STDMETHODIMP GiveFeedback( DWORD ) FL_OVERRIDE { return DRAGDROP_S_USEDEFAULTCURSORS; } - STDMETHODIMP QueryContinueDrag( BOOL esc, DWORD keyState ) FL_OVERRIDE { - if ( esc ) - return DRAGDROP_S_CANCEL; - if ( !(keyState & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) ) - return DRAGDROP_S_DROP; - return S_OK; - } -}; -class FLEnum : public IEnumFORMATETC -{ -public: - int n; - LONG m_lRefCount; - - ULONG __stdcall AddRef(void) FL_OVERRIDE { - return InterlockedIncrement(&m_lRefCount); - } - - ULONG __stdcall Release(void) FL_OVERRIDE { - LONG count = InterlockedDecrement(&m_lRefCount); - if(count == 0) { - delete this; - return 0; - } else { - return count; - } - } - - - HRESULT __stdcall QueryInterface(REFIID iid, void **ppvObject) FL_OVERRIDE { - if(iid == IID_IEnumFORMATETC || iid == IID_IUnknown) { - AddRef(); - *ppvObject = this; - return S_OK; - } else { - *ppvObject = 0; - return E_NOINTERFACE; - } - } - - HRESULT __stdcall Next(ULONG celt, FORMATETC * rgelt, ULONG *pceltFetched) FL_OVERRIDE { - if (n > 0) return S_FALSE; - for (ULONG i = 0; i < celt; i++) { - n++; - rgelt->cfFormat = CF_HDROP; - rgelt->dwAspect = DVASPECT_CONTENT; - rgelt->lindex = -1; - rgelt->ptd = NULL; - rgelt->tymed = TYMED_HGLOBAL; - } - if (pceltFetched) *pceltFetched = celt; - return S_OK; - } - - HRESULT __stdcall Skip(ULONG celt) FL_OVERRIDE { - n += celt; - return (n == 0) ? S_OK : S_FALSE; - } - - HRESULT __stdcall Reset(void) FL_OVERRIDE { - n = 0; - return S_OK; - } - - HRESULT __stdcall Clone(IEnumFORMATETC **ppenum) FL_OVERRIDE { - *ppenum = new FLEnum(); - return S_OK; - } - - FLEnum(void) { - m_lRefCount = 1; - n = 0; - } - - virtual ~FLEnum(void) { - n = 0; - } -}; - - -/** - This is the actual object that FLTK can drop somewhere. - - The implementation is minimal, but it should work with all decent Win32 drop targets -*/ -class FLDataObject : public IDataObject -{ - DWORD m_cRefCount; - //FLEnum *m_EnumF; -public: - FLDataObject() { m_cRefCount = 1; }// m_EnumF = new FLEnum();} - virtual ~FLDataObject() { } - HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, LPVOID *ppvObject ) FL_OVERRIDE { - if (IID_IUnknown==riid || IID_IDataObject==riid) - { - *ppvObject=this; - ((LPUNKNOWN)*ppvObject)->AddRef(); - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - ULONG STDMETHODCALLTYPE AddRef() FL_OVERRIDE { return ++m_cRefCount; } - ULONG STDMETHODCALLTYPE Release() FL_OVERRIDE { - long nTemp; - nTemp = --m_cRefCount; - if(nTemp==0) - delete this; - return nTemp; - } - // GetData currently allows UNICODE text through Global Memory only - HRESULT STDMETHODCALLTYPE GetData( FORMATETC *pformatetcIn, STGMEDIUM *pmedium ) FL_OVERRIDE { - if ((pformatetcIn->dwAspect & DVASPECT_CONTENT) && - (pformatetcIn->tymed & TYMED_HGLOBAL) && - (pformatetcIn->cfFormat == CF_UNICODETEXT)) - { - int utf16_len = fl_utf8toUtf16(fl_selection_buffer[0], fl_selection_length[0], 0, 0); - HGLOBAL gh = GlobalAlloc( GHND, utf16_len * 2 + 2 ); - char *pMem = (char*)GlobalLock( gh ); - fl_utf8toUtf16(fl_selection_buffer[0], fl_selection_length[0], (unsigned short*)pMem, utf16_len + 1); -// HGLOBAL gh = GlobalAlloc( GHND| GMEM_SHARE, -// (fl_selection_length[0]+4) * sizeof(short) -// + sizeof(DROPFILES)); -// unsigned char *pMem = (unsigned char*)GlobalLock( gh ); -// if (!pMem) { -// GlobalFree(gh); -// return DV_E_FORMATETC; -// } -// DROPFILES *df =(DROPFILES*) pMem; -// int l; -// df->pFiles = sizeof(DROPFILES); -// df->pt.x = 0; -// df->pt.y = 0; -// df->fNC = FALSE; -// for (int i = 0; i < fl_selection_length[0]; i++) { -// if (fl_selection_buffer[0][i] == '\n') { -// fl_selection_buffer[0][i] = '\0'; -// } -// } -// -// df->fWide = TRUE; -// l = fl_utf2unicode((unsigned char*)fl_selection_buffer[0], -// fl_selection_length[0], (wchar_t*)(((char*)pMem) -// + sizeof(DROPFILES))); -// -// pMem[l * sizeof(WCHAR) + sizeof(DROPFILES)] = 0; -// pMem[l * sizeof(WCHAR) + 1 + sizeof(DROPFILES)] = 0; -// pMem[l * sizeof(WCHAR) + 2 + sizeof(DROPFILES)] = 0; -// pMem[l * sizeof(WCHAR) + 3 + sizeof(DROPFILES)] = 0; - pmedium->tymed = TYMED_HGLOBAL; - pmedium->hGlobal = gh; - pmedium->pUnkForRelease = NULL; - GlobalUnlock( gh ); - return S_OK; - } - return DV_E_FORMATETC; - } - HRESULT STDMETHODCALLTYPE QueryGetData( FORMATETC *pformatetc ) FL_OVERRIDE - { - if ((pformatetc->dwAspect & DVASPECT_CONTENT) && - (pformatetc->tymed & TYMED_HGLOBAL) && - (pformatetc->cfFormat == CF_UNICODETEXT)) - return S_OK; - return DV_E_FORMATETC; - } -// HRESULT STDMETHODCALLTYPE EnumFormatEtc( DWORD dir, IEnumFORMATETC** ppenumFormatEtc) { -// *ppenumFormatEtc = m_EnumF; -// return S_OK; -// } - - // all the following methods are not really needed for a DnD object - HRESULT STDMETHODCALLTYPE GetDataHere( FORMATETC* /*pformatetcIn*/, STGMEDIUM* /*pmedium*/) FL_OVERRIDE { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc( FORMATETC* /*in*/, FORMATETC* /*out*/) FL_OVERRIDE { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE SetData( FORMATETC* /*pformatetc*/, STGMEDIUM* /*pmedium*/, BOOL /*fRelease*/) FL_OVERRIDE { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE EnumFormatEtc( DWORD /*dir*/, IEnumFORMATETC** /*ppenumFormatEtc*/) FL_OVERRIDE { return E_NOTIMPL; } -// HRESULT STDMETHODCALLTYPE EnumFormatEtc( DWORD dir, IEnumFORMATETC** ppenumFormatEtc) FL_OVERRIDE {*ppenumFormatEtc = m_EnumF; return S_OK;} - HRESULT STDMETHODCALLTYPE DAdvise( FORMATETC* /*pformatetc*/, DWORD /*advf*/, - IAdviseSink* /*pAdvSink*/, DWORD* /*pdwConnection*/) FL_OVERRIDE { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE DUnadvise( DWORD /*dwConnection*/) FL_OVERRIDE { return E_NOTIMPL; } - HRESULT STDMETHODCALLTYPE EnumDAdvise( IEnumSTATDATA** /*ppenumAdvise*/) FL_OVERRIDE { return E_NOTIMPL; } -}; - - -int Fl_WinAPI_Screen_Driver::dnd(int unused) -{ - DWORD dropEffect; - ReleaseCapture(); - - FLDataObject *fdo = new FLDataObject; - fdo->AddRef(); - FLDropSource *fds = new FLDropSource; - fds->AddRef(); - - HRESULT ret = DoDragDrop( fdo, fds, DROPEFFECT_MOVE|DROPEFFECT_LINK|DROPEFFECT_COPY, &dropEffect ); - - fdo->Release(); - fds->Release(); - - Fl_Widget *w = Fl::pushed(); - if ( w ) - { - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_RELEASE); - Fl::e_number = old_event; - Fl::pushed( 0 ); - } - if ( ret==DRAGDROP_S_DROP ) return 1; // or DD_S_CANCEL - return 0; -} diff --git a/src/fl_encoding_latin1.cxx b/src/fl_encoding_latin1.cxx index 23bd889d7..7ec86f14a 100644 --- a/src/fl_encoding_latin1.cxx +++ b/src/fl_encoding_latin1.cxx @@ -70,3 +70,16 @@ const char *fl_local_to_latin1(const char *t, int n) { return Fl::system_driver()->local_to_latin1(t, n); } + +// Stub implementations for mac_roman encoding functions. +// On X11/Unix, these are no-ops since mac_roman encoding is not used. + +const char *Fl_System_Driver::mac_roman_to_local(const char *t, int) +{ + return t; +} + +const char *Fl_System_Driver::local_to_mac_roman(const char *t, int) +{ + return t; +} diff --git a/src/fl_encoding_mac_roman.cxx b/src/fl_encoding_mac_roman.cxx deleted file mode 100644 index 75439a3ee..000000000 --- a/src/fl_encoding_mac_roman.cxx +++ /dev/null @@ -1,117 +0,0 @@ -// -// Convert Mac Roman encoded text to the local encoding. -// -// Copyright 1998-2021 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -#include -#include -#include "Fl_System_Driver.H" -#include -#include -#include -#include "flstring.h" - -/** - \cond DriverDev - \addtogroup DriverDeveloper - \{ - */ - -// These function assume a western code page. -// -// Windows and X11 render text in ISO or Latin-1 for western settings. The -// lookup tables below will convert all common character codes and replace -// unknown characters with an upsidedown question mark. - -// This table converts Windows-1252/Latin 1 into MacRoman encoding -static uchar latin2roman[128] = { -0xdb, 0xc0, 0xe2, 0xc4, 0xe3, 0xc9, 0xa0, 0xe0, 0xf6, 0xe4, 0xc0, 0xdc, 0xce, 0xc0, 0xc0, 0xc0, -0xc0, 0xd4, 0xd5, 0xd2, 0xd3, 0xa5, 0xd0, 0xd1, 0xf7, 0xaa, 0xc0, 0xdd, 0xcf, 0xc0, 0xc0, 0xd9, -0xca, 0xc1, 0xa2, 0xa3, 0xc0, 0xb4, 0xc0, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xc0, 0xa8, 0xf8, -0xa1, 0xb1, 0xc0, 0xc0, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xc0, 0xbc, 0xc8, 0xc0, 0xc0, 0xc0, 0xc0, -0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, -0xc0, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xc0, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xc0, 0xc0, 0xa7, -0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, -0xc0, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xc0, 0xc0, 0xd8 -}; - -// This table converts MacRoman into Windows-1252/Latin 1 -static uchar roman2latin[128] = { -0xc4, 0xc5, 0xc7, 0xc9, 0xd1, 0xd6, 0xdc, 0xe1, 0xe0, 0xe2, 0xe4, 0xe3, 0xe5, 0xe7, 0xe9, 0xe8, -0xea, 0xeb, 0xed, 0xec, 0xee, 0xef, 0xf1, 0xf3, 0xf2, 0xf4, 0xf6, 0xf5, 0xfa, 0xf9, 0xfb, 0xfc, -0x86, 0xb0, 0xa2, 0xa3, 0xa7, 0x95, 0xb6, 0xdf, 0xae, 0xa9, 0x99, 0xb4, 0xa8, 0xbf, 0xc6, 0xd8, -0xbf, 0xb1, 0xbf, 0xbf, 0xa5, 0xb5, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xaa, 0xba, 0xbf, 0xe6, 0xf8, -0xbf, 0xa1, 0xac, 0xbf, 0x83, 0xbf, 0xbf, 0xab, 0xbb, 0x85, 0xa0, 0xc0, 0xc3, 0xd5, 0x8c, 0x9c, -0x96, 0x97, 0x93, 0x94, 0x91, 0x92, 0xf7, 0xbf, 0xff, 0x9f, 0xbf, 0x80, 0x8b, 0x9b, 0xbf, 0xbf, -0x87, 0xb7, 0x82, 0x84, 0x89, 0xc2, 0xca, 0xc1, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xd3, 0xd4, -0xbf, 0xd2, 0xda, 0xdb, 0xd9, 0xbf, 0x88, 0x98, 0xaf, 0xbf, 0xbf, 0xbf, 0xb8, 0xbf, 0xbf, 0xbf -}; - -static char *buf = 0; -static int n_buf = 0; - -const char *Fl_System_Driver::local_to_mac_roman(const char *t, int n) -{ - if (n==-1) n = (int) strlen(t); - if (n<=n_buf) { - n_buf = (n + 257) & 0x7fffff00; - if (buf) free(buf); - buf = (char*)malloc(n_buf); - } - const uchar *src = (const uchar*)t; - uchar *dst = (uchar*)buf; - for ( ; n>0; n--) { - uchar c = *src; - if (c>127) - *dst = latin2roman[c-128]; - else - *dst = c; - } - //*dst = 0; // this would be wrong! - return buf; -} - -const char *Fl_System_Driver::mac_roman_to_local(const char *t, int n) -{ - if (n==-1) n = (int) strlen(t); - if (n<=n_buf) { - n_buf = (n + 257) & 0x7fffff00; - if (buf) free(buf); - buf = (char*)malloc(n_buf); - } - const uchar *src = (const uchar*)t; - uchar *dst = (uchar*)buf; - for ( ; n>0; n--) { - uchar c = *src++; - if (c>127) - *dst++ = roman2latin[c-128]; - else - *dst++ = c; - } - //*dst = 0; // this would be wrong - return buf; -} - -/** - \} - \endcond - */ - -const char *fl_local_to_mac_roman(const char *t, int n) { - return Fl::system_driver()->local_to_mac_roman(t, n); -} - -const char *fl_mac_roman_to_local(const char *t, int n) { - return Fl::system_driver()->mac_roman_to_local(t, n); -} diff --git a/src/flstring.h b/src/flstring.h index 04199d13e..5cedf1711 100644 --- a/src/flstring.h +++ b/src/flstring.h @@ -53,11 +53,6 @@ * Some of these functions are also defined in ISO C99... */ -# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define strcasecmp(s,t) _stricmp((s), (t)) -# define strncasecmp(s,t,n) _strnicmp((s), (t), (n)) -# endif /* _WIN32 && ... */ - # ifdef __cplusplus extern "C" { # endif /* __cplusplus */ diff --git a/src/forms_fselect.cxx b/src/forms_fselect.cxx index fd16b4d16..82e397ec4 100644 --- a/src/forms_fselect.cxx +++ b/src/forms_fselect.cxx @@ -29,11 +29,8 @@ char* fl_show_file_selector(const char *message,const char *dir, if (pat && pat[0]) fl_pattern = pat; if (fname && fname[0]) strlcpy(fl_filename,fname,sizeof(fl_filename)); char *p = fl_directory+strlen(fl_directory); - if (p > fl_directory && *(p-1)!='/' -#ifdef _WIN32 - && *(p-1)!='\\' && *(p-1)!=':' -#endif - ) *p++ = '/'; + if (p > fl_directory && *(p-1)!='/') + *p++ = '/'; strlcpy(p,fl_filename,sizeof(fl_directory) - (p - fl_directory)); const char *q = fl_file_chooser(message,fl_pattern,fl_directory); if (!q) return 0; diff --git a/src/scandir_win32.c b/src/scandir_win32.c deleted file mode 100644 index 4c9fc4fc9..000000000 --- a/src/scandir_win32.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Windows scandir function for the Fast Light Tool Kit (FLTK). - * - * Copyright 1998-2023 by Bill Spitzak and others. - * - * This library is free software. Distribution and use rights are outlined in - * the file "COPYING" which should have been included with this file. If this - * file is missing or damaged, see the license at: - * - * https://www.fltk.org/COPYING.php - * - * Please see the following page on how to report bugs and issues: - * - * https://www.fltk.org/bugs.php - */ - -#ifndef __CYGWIN__ -/* Emulation of POSIX scandir() call with error messages */ -#include -#include -#include "flstring.h" -#include -#include - -/* Get error message string for last failed WIN32 operation - * in 'errmsg' (if non-NULL), string size limited to errmsg_sz. - * - * NOTE: Copied from: fluid/ExternalCodeEditor_WIN32.cxx - * - * TODO: Verify works in different languages, with utf8 strings. - * TODO: This should be made available globally to the FLTK internals, in case - * other parts of FLTK need OS error messages.. - */ -static void get_ms_errmsg(char *errmsg, int errmsg_sz) { - DWORD lastErr = GetLastError(); - DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM; - DWORD langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); - LPWSTR mbuf = 0; - DWORD msize = 0; - - /* Early exit if parent doesn't want an errmsg */ - if (!errmsg || errmsg_sz <= 0 ) return; - /* Get error message from Windows */ - msize = FormatMessageW(flags, 0, lastErr, langid, (LPWSTR)&mbuf, 0, NULL); - if ( msize == 0 ) { - fl_snprintf(errmsg, errmsg_sz, "Error #%lu", (unsigned long)lastErr); - } else { - char *src; - char *dst; - /* convert message to UTF-8 */ - fl_utf8fromwc(errmsg, errmsg_sz, mbuf, msize); - /* Remove '\r's -- they screw up fl_alert()) */ - src = dst = errmsg; - for ( ; 1; src++ ) { - if ( *src == '\0' ) { *dst = '\0'; break; } - if ( *src != '\r' ) { *dst++ = *src; } - } - LocalFree(mbuf); /* Free the buffer allocated by the system */ - } -} - -/* - * This could use some docs. - * - * Returns -1 on error, errmsg returns error string (if non-NULL) - */ -int fl_scandir(const char *dirname, struct dirent ***namelist, - int (*select)(struct dirent *), - int (*compar)(struct dirent **, struct dirent **), - char *errmsg, int errmsg_sz) { - int len; - char *findIn, *d, is_dir = 0; - WIN32_FIND_DATAW findw; - HANDLE h; - int nDir = 0, NDir = 0; - struct dirent **dir = 0, *selectDir; - unsigned long ret; - - if (errmsg && errmsg_sz>0) errmsg[0] = '\0'; - len = (int) strlen(dirname); - findIn = (char *)malloc((size_t)(len+10)); - if (!findIn) { - /* win32 malloc() docs: "malloc sets errno to ENOMEM if allocation fails" */ - if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); - return -1; - } - strcpy(findIn, dirname); - - for (d = findIn; *d; d++) if (*d == '/') *d = '\\'; - if (len == 0) { strcpy(findIn, ".\\*"); } - if ((len == 2) && (findIn[1] == ':') && isalpha(findIn[0])) { *d++ = '\\'; *d = 0; } - if ((len == 1) && (d[-1] == '.')) { strcpy(findIn, ".\\*"); is_dir = 1; } - if ((len > 0) && (d[-1] == '\\')) { *d++ = '*'; *d = 0; is_dir = 1; } - if ((len > 1) && (d[-1] == '.') && (d[-2] == '\\')) { d[-1] = '*'; is_dir = 1; } - { /* Create a block to limit the scope while we find the initial "wide" filename */ - unsigned short *wbuf = NULL; - unsigned wlen = fl_utf8toUtf16(findIn, (unsigned) strlen(findIn), NULL, 0); /* Pass NULL to query length */ - wlen++; /* add a little extra for termination etc. */ - wbuf = (unsigned short*)malloc(sizeof(unsigned short)*(wlen+2)); - wlen = fl_utf8toUtf16(findIn, (unsigned) strlen(findIn), wbuf, wlen); /* actually convert the filename */ - wbuf[wlen] = 0; /* NULL terminate the resultant string */ - if (!is_dir) { /* this file may still be a directory that we need to list */ - DWORD attr = GetFileAttributesW(wbuf); - if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) ) { - wbuf[wlen] = '\\'; wbuf[wlen+1] = '*'; wbuf[wlen+2] = 0; - } - } - h = FindFirstFileW(wbuf, &findw); /* get a handle to the first filename in the search */ - free(wbuf); /* release the "wide" buffer before the pointer goes out of scope */ - } - - if (h == INVALID_HANDLE_VALUE) { - free(findIn); - ret = GetLastError(); - if (ret != ERROR_NO_MORE_FILES) { - nDir = -1; - get_ms_errmsg(errmsg, errmsg_sz); /* return OS error msg */ - } - *namelist = dir; - return nDir; - } - do { - int l = (int) wcslen(findw.cFileName); - int dstlen = l * 5 + 1; - selectDir=(struct dirent*)malloc(sizeof(struct dirent)+dstlen); - - l = fl_utf8fromwc(selectDir->d_name, dstlen, findw.cFileName, l); - - selectDir->d_name[l] = 0; - if (findw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - /* Append a trailing slash to directory names... */ - strcat(selectDir->d_name, "/"); - } - if (!select || (*select)(selectDir)) { - if (nDir==NDir) { - struct dirent **tempDir = (struct dirent **)calloc(sizeof(struct dirent*), (size_t)(NDir+33)); - if (NDir) memcpy(tempDir, dir, sizeof(struct dirent*)*NDir); - if (dir) free(dir); - dir = tempDir; - NDir += 32; - } - dir[nDir] = selectDir; - nDir++; - dir[nDir] = 0; - } else { - free(selectDir); - } - } while (FindNextFileW(h, &findw)); - ret = GetLastError(); - if (ret != ERROR_NO_MORE_FILES) { - /* don't return an error code, because the dir list may still be valid - up to this point */ - } - FindClose(h); - free (findIn); - if (compar) qsort(dir, (size_t)nDir, sizeof(*dir), - (int(*)(const void*, const void*))compar); - *namelist = dir; - return nDir; -} - -#endif -- cgit v1.2.3